boba 0.2.0 → 0.3.0
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0a005078d3a776680cbc55c160a934a0460ba4cb7b5dd51de9c446eded2c9cdd
|
|
4
|
+
data.tar.gz: fbed64ed0a040d65e84ffa0836ab74500a3f421d0e20e13b4e00f091037cd917
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 58cbdd3b0b5933e5052dc888dd4c596e3ee854890e6bf4c6dca431b16e71dd098828663c266d0239768c5c2493ac0430fde363cf1020e2f17ea902657d61b2b2
|
|
7
|
+
data.tar.gz: 9b8e2cc9d3c511bbc420152b2f53b73c16c3fbe0653e4adc4e1ca9f67664f90066957e7cf8cf8317b4bbb4553548171e6ebe264bc3e4910e7a9031e27c8ec6c8
|
|
@@ -27,10 +27,8 @@ module Boba
|
|
|
27
27
|
false
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
#: (
|
|
30
|
+
#: (::ActiveModel::Validations::ClassMethods constant, String attribute) -> bool
|
|
31
31
|
def has_unconditional_presence_validator?(constant, attribute)
|
|
32
|
-
return false unless constant.respond_to?(:validators_on)
|
|
33
|
-
|
|
34
32
|
constant.validators_on(attribute).any? do |validator|
|
|
35
33
|
unconditional_presence_validator?(validator)
|
|
36
34
|
end
|
|
@@ -40,7 +38,7 @@ module Boba
|
|
|
40
38
|
|
|
41
39
|
#: (ActiveModel::Validator validator) -> bool
|
|
42
40
|
def unconditional_presence_validator?(validator)
|
|
43
|
-
return false unless validator.is_a?(::
|
|
41
|
+
return false unless validator.is_a?(::ActiveModel::Validations::PresenceValidator)
|
|
44
42
|
|
|
45
43
|
unconditional_validator?(validator)
|
|
46
44
|
end
|
data/lib/boba/version.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
return unless defined?(ActsAsTaggableOn)
|
|
5
5
|
|
|
6
|
+
require "tapioca/dsl/helpers/active_record_constants_helper"
|
|
7
|
+
|
|
6
8
|
module Tapioca
|
|
7
9
|
module Dsl
|
|
8
10
|
module Compilers
|
|
@@ -68,13 +70,37 @@ module Tapioca
|
|
|
68
70
|
# sig { params(limit: Integer).returns(T.untyped) }
|
|
69
71
|
# def top_tags(limit = 10); end
|
|
70
72
|
# end
|
|
73
|
+
#
|
|
74
|
+
# module GeneratedAssociationRelationMethods
|
|
75
|
+
# sig { params(context: T.untyped, options: T.untyped).returns(::ActsAsTaggableOn::Tag::PrivateRelation) }
|
|
76
|
+
# def tag_counts_on(context, options = {}); end
|
|
77
|
+
#
|
|
78
|
+
# sig { params(tags: T.untyped, options: T.untyped).returns(PrivateAssociationRelation) }
|
|
79
|
+
# def tagged_with(tags, options = {}); end
|
|
80
|
+
# end
|
|
81
|
+
#
|
|
82
|
+
# module GeneratedRelationMethods
|
|
83
|
+
# sig { params(context: T.untyped, options: T.untyped).returns(::ActsAsTaggableOn::Tag::PrivateRelation) }
|
|
84
|
+
# def tag_counts_on(context, options = {}); end
|
|
85
|
+
#
|
|
86
|
+
# sig { params(tags: T.untyped, options: T.untyped).returns(PrivateRelation) }
|
|
87
|
+
# def tagged_with(tags, options = {}); end
|
|
88
|
+
# end
|
|
71
89
|
# end
|
|
72
90
|
# ~~~
|
|
73
91
|
#
|
|
74
92
|
# The mixins are declared rather than re-implemented, so `tagged_with`, `tag_list_on` and the rest keep
|
|
75
93
|
# the signatures they have in the gem RBI. Only the per-context methods, which exist for the contexts of
|
|
76
94
|
# this model alone, are generated.
|
|
95
|
+
#
|
|
96
|
+
# The relation side is the exception. `ActiveRecord::Relation` reaches the class methods by delegating to
|
|
97
|
+
# the model class, which Sorbet cannot follow, so `Post.published.tagged_with(...)` does not type-check at
|
|
98
|
+
# all. The two finders that answer a relation are re-stated on the relation modules: `tagged_with`
|
|
99
|
+
# narrows the receiver, and `tag_counts_on` answers a relation of `ActsAsTaggableOn::Tag` rather than
|
|
100
|
+
# of the model. Without the relations compiler both stay untyped.
|
|
77
101
|
class ActsAsTaggableOn < Tapioca::Dsl::Compiler
|
|
102
|
+
include Helpers::ActiveRecordConstantsHelper
|
|
103
|
+
|
|
78
104
|
ConstantType = type_member { { fixed: T.class_of(::ActiveRecord::Base) } }
|
|
79
105
|
|
|
80
106
|
# Every `acts_as_taggable_on` call installs this set; the names are fixed, so there is nothing to
|
|
@@ -96,6 +122,8 @@ module Tapioca
|
|
|
96
122
|
tag_types.each do |tag_type|
|
|
97
123
|
create_context_methods(model, tag_type)
|
|
98
124
|
end
|
|
125
|
+
|
|
126
|
+
create_relation_methods(model)
|
|
99
127
|
end
|
|
100
128
|
end
|
|
101
129
|
|
|
@@ -111,6 +139,36 @@ module Tapioca
|
|
|
111
139
|
|
|
112
140
|
private
|
|
113
141
|
|
|
142
|
+
#: (RBI::Scope model) -> void
|
|
143
|
+
def create_relation_methods(model)
|
|
144
|
+
relations_typed = compiler_enabled?("ActiveRecordRelations")
|
|
145
|
+
tag_relation = relations_typed ? "::ActsAsTaggableOn::Tag::PrivateRelation" : "T.untyped"
|
|
146
|
+
|
|
147
|
+
[
|
|
148
|
+
[RelationMethodsModuleName, RelationClassName],
|
|
149
|
+
[AssociationRelationMethodsModuleName, AssociationRelationClassName],
|
|
150
|
+
].each do |module_name, relation_class|
|
|
151
|
+
relation_methods_module = model.create_module(module_name)
|
|
152
|
+
|
|
153
|
+
relation_methods_module.create_method(
|
|
154
|
+
"tagged_with",
|
|
155
|
+
parameters: [
|
|
156
|
+
create_param("tags", type: "T.untyped"),
|
|
157
|
+
create_opt_param("options", type: "T.untyped", default: "{}"),
|
|
158
|
+
],
|
|
159
|
+
return_type: relations_typed ? relation_class : "T.untyped",
|
|
160
|
+
)
|
|
161
|
+
relation_methods_module.create_method(
|
|
162
|
+
"tag_counts_on",
|
|
163
|
+
parameters: [
|
|
164
|
+
create_param("context", type: "T.untyped"),
|
|
165
|
+
create_opt_param("options", type: "T.untyped", default: "{}"),
|
|
166
|
+
],
|
|
167
|
+
return_type: tag_relation,
|
|
168
|
+
)
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
114
172
|
#: (RBI::Scope model, String tag_type) -> void
|
|
115
173
|
def create_context_methods(model, tag_type)
|
|
116
174
|
singular = tag_type.singularize
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
return unless defined?(AttrJson::Record)
|
|
5
5
|
|
|
6
|
+
require "boba/active_record/attribute_service"
|
|
7
|
+
|
|
6
8
|
module Tapioca
|
|
7
9
|
module Dsl
|
|
8
10
|
module Compilers
|
|
@@ -25,14 +27,20 @@ module Tapioca
|
|
|
25
27
|
# extend AttrJson::Record::ClassMethods
|
|
26
28
|
#
|
|
27
29
|
# module AttrJsonGeneratedMethods
|
|
28
|
-
# sig { returns(::Integer) }
|
|
30
|
+
# sig { returns(T.nilable(::Integer)) }
|
|
29
31
|
# def price_cents; end
|
|
30
32
|
#
|
|
31
|
-
# sig { params(value: Integer).returns(::Integer) }
|
|
33
|
+
# sig { params(value: T.nilable(::Integer)).returns(T.nilable(::Integer)) }
|
|
32
34
|
# def price_cents=(value); end
|
|
33
35
|
# end
|
|
34
36
|
# end
|
|
35
37
|
# ~~~
|
|
38
|
+
#
|
|
39
|
+
# A scalar attribute reads as `nil` until it is set, so it is typed as nilable unless it has a non-nil
|
|
40
|
+
# `default:`. As with columns in `ActiveRecordColumnsPersisted`, an unconditional presence validator
|
|
41
|
+
# (no `if:`, `unless:` or `on:`) types it as it is on a valid record instead:
|
|
42
|
+
# `validates :price_cents, presence: true` turns the methods above into `returns(::Integer)` and
|
|
43
|
+
# `params(value: ::Integer)`.
|
|
36
44
|
class AttrJson < Tapioca::Dsl::Compiler
|
|
37
45
|
# Class methods module is already defined in the gem rbi, so just reference it here.
|
|
38
46
|
ClassMethodsModuleName = "AttrJson::Record::ClassMethods"
|
|
@@ -63,13 +71,27 @@ module Tapioca
|
|
|
63
71
|
private
|
|
64
72
|
|
|
65
73
|
def decorate_attributes(rbi_scope)
|
|
74
|
+
# Both AttrJson::Record and AttrJson::Model bring in ActiveModel validations; the check lets Sorbet see it.
|
|
75
|
+
klass = constant
|
|
76
|
+
validated = klass if klass.is_a?(::ActiveModel::Validations::ClassMethods)
|
|
77
|
+
|
|
66
78
|
constant.attr_json_registry
|
|
67
79
|
.definitions
|
|
68
80
|
.sort_by(&:name) # this is annoying, but we need to sort to force consistent ordering or the rbi checks fail
|
|
69
81
|
.each do |definition|
|
|
70
82
|
_, type, options = definition.original_args
|
|
71
83
|
attribute_name = definition.name.to_s
|
|
72
|
-
|
|
84
|
+
array = !!options[:array]
|
|
85
|
+
# AttrJson has no option that forbids nil: an attribute reads as nil unless it has a default,
|
|
86
|
+
# which AttrJson also fills in when a stored record lacks the key. An array one defaults to []
|
|
87
|
+
# on its own unless the default is overridden. As with columns, an unconditional presence
|
|
88
|
+
# validator types the attribute as it is on a valid record.
|
|
89
|
+
nilable = array ? options.key?(:default) && options[:default].nil? : options[:default].nil?
|
|
90
|
+
nilable &&= !(validated && Boba::ActiveRecord::AttributeService.has_unconditional_presence_validator?(
|
|
91
|
+
validated,
|
|
92
|
+
attribute_name,
|
|
93
|
+
))
|
|
94
|
+
type_name = sorbet_type(type, array: array, nilable: nilable)
|
|
73
95
|
|
|
74
96
|
# Model: attr_json(:other_model_id, :string)
|
|
75
97
|
# => other_model_id
|
|
@@ -39,12 +39,19 @@ module Tapioca
|
|
|
39
39
|
# # typed: true
|
|
40
40
|
# class Chat
|
|
41
41
|
# include RubyLLM::ActiveRecord::ChatMethods
|
|
42
|
+
#
|
|
43
|
+
# Elem = type_member { { fixed: ::RubyLLM::Message } }
|
|
42
44
|
# end
|
|
43
45
|
# ~~~
|
|
44
46
|
#
|
|
45
47
|
# The module is declared rather than re-implemented, so `ask`, `with_instructions`,
|
|
46
48
|
# `with_runtime_instructions` and the rest keep the signatures they have in the gem RBI. Models that
|
|
47
49
|
# never call an `acts_as_*` method are left alone.
|
|
50
|
+
#
|
|
51
|
+
# Since ruby_llm 2.0 `ChatMethods` includes `Enumerable` and hands `each` over to the underlying
|
|
52
|
+
# `RubyLLM::Chat`, which yields `RubyLLM::Message`s. A class that includes a generic module has to
|
|
53
|
+
# re-declare its type members, so the chat model gets `Elem` fixed to that; on older versions,
|
|
54
|
+
# where the module is not enumerable, nothing is added.
|
|
48
55
|
class RubyLLM < Tapioca::Dsl::Compiler
|
|
49
56
|
ConstantType = type_member { { fixed: T.class_of(::ActiveRecord::Base) } }
|
|
50
57
|
|
|
@@ -66,9 +73,20 @@ module Tapioca
|
|
|
66
73
|
|
|
67
74
|
root.create_path(constant) do |model|
|
|
68
75
|
mixins.each { |name| model.create_include(name) }
|
|
76
|
+
|
|
77
|
+
add_message_elem_type_member(model, mixins)
|
|
69
78
|
end
|
|
70
79
|
end
|
|
71
80
|
|
|
81
|
+
private
|
|
82
|
+
|
|
83
|
+
#: (RBI::Scope model, Array[String] mixins) -> void
|
|
84
|
+
def add_message_elem_type_member(model, mixins)
|
|
85
|
+
return unless constant < ::Enumerable && mixins.include?("RubyLLM::ActiveRecord::ChatMethods")
|
|
86
|
+
|
|
87
|
+
model.create_type_variable("Elem", type: "type_member", fixed: "::RubyLLM::Message")
|
|
88
|
+
end
|
|
89
|
+
|
|
72
90
|
class << self
|
|
73
91
|
# @override
|
|
74
92
|
#: -> Enumerable[Module[top]]
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: boba
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexander Stathis
|
|
@@ -74,9 +74,9 @@ licenses:
|
|
|
74
74
|
- MIT
|
|
75
75
|
metadata:
|
|
76
76
|
bug_tracker_uri: https://github.com/stathis-alexander/boba/issues
|
|
77
|
-
changelog_uri: https://github.com/stathis-alexander/boba/blob/0.
|
|
77
|
+
changelog_uri: https://github.com/stathis-alexander/boba/blob/0.3.0/History.md
|
|
78
78
|
homepage_uri: https://github.com/stathis-alexander/boba
|
|
79
|
-
source_code_uri: https://github.com/stathis-alexander/boba/tree/0.
|
|
79
|
+
source_code_uri: https://github.com/stathis-alexander/boba/tree/0.3.0
|
|
80
80
|
rubygems_mfa_required: 'true'
|
|
81
81
|
rdoc_options: []
|
|
82
82
|
require_paths:
|