boba 0.0.6 → 0.0.8

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: 8819547e35a1566b8b772069f87ae189a4d1faa0bc7b3426e69c01b286fb372b
4
- data.tar.gz: 23e40a45b04a32ee32322d2123eabb3dc9e06829df556af916b1a2885cb04168
3
+ metadata.gz: c574e47f38c05d56093ec30f3f1328e28440c4ad2511567e0015106798569e44
4
+ data.tar.gz: cdeb013e429306a83dd5a41023fb73c311180d88e89923cccba55eff5a613dda
5
5
  SHA512:
6
- metadata.gz: a71b576ebd62ccfb4cde1a8383ad3dfccabb7a1f6812f35ad54ca8f3def3d1c014c493d86d7a973939c6f425a3a2eeebd62ec77df5c94b91840c1ada25b283d1
7
- data.tar.gz: '0417790d2c32e3faa556a22b6a9deab60581f3b6534f04451c87b89e74b4aa67ef0e3608eaf782a2343d83b1319e7461e3ca841a842dd557dda7ba83c78db4d2'
6
+ metadata.gz: 93de6c75462a9824d8cacd0fc0fec7ae246694eb0c63acf216cc0f04f3eb4fee94e9b98a77b6e65879eb8c11d41bf47fc06dae535b4767ecbab15150cc0c43ec
7
+ data.tar.gz: 2de09551f27c892aa5317aa133f30e67b70dfa9bfbc16e7c4474e089909ba7d3bf7d1178109f94f48b09a17d774425b3d51e1eb778629b2834135d1a66daad61
@@ -0,0 +1,38 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Boba
5
+ module ActiveRecord
6
+ module AttributeService
7
+ class << self
8
+ extend T::Sig
9
+
10
+ sig { params(constant: T.class_of(::ActiveRecord::Base), attribute: String).returns(T::Boolean) }
11
+ def has_unconditional_presence_validator?(constant, attribute)
12
+ return false unless constant.respond_to?(:validators_on)
13
+
14
+ constant.validators_on(attribute).any? do |validator|
15
+ next false unless validator.is_a?(::ActiveRecord::Validations::PresenceValidator)
16
+
17
+ !validator.options.key?(:if) && !validator.options.key?(:unless) && !validator.options.key?(:on)
18
+ end
19
+ end
20
+
21
+ sig { params(constant: T.class_of(::ActiveRecord::Base), column_name: String).returns(T::Boolean) }
22
+ def has_non_null_database_constraint?(constant, column_name)
23
+ column = constant.columns_hash[column_name]
24
+ return false if column.nil?
25
+
26
+ !column.null
27
+ rescue StandardError
28
+ false
29
+ end
30
+
31
+ sig { params(constant: T.class_of(::ActiveRecord::Base), column_name: String).returns(T::Boolean) }
32
+ def virtual_attribute?(constant, column_name)
33
+ constant.columns_hash[column_name].nil?
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,69 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require_relative("attribute_service")
5
+
6
+ module Boba
7
+ module ActiveRecord
8
+ module ReflectionService
9
+ class << self
10
+ extend T::Sig
11
+
12
+ ReflectionType = T.type_alias do
13
+ T.any(::ActiveRecord::Reflection::ThroughReflection, ::ActiveRecord::Reflection::AssociationReflection)
14
+ end
15
+
16
+ sig { params(reflection: ReflectionType).returns(T::Boolean) }
17
+ def has_one_and_required_reflection?(reflection)
18
+ return false unless reflection.has_one?
19
+ return true if !!reflection.options[:required]
20
+ return true if reflection_required_by_database_constraint?(reflection)
21
+
22
+ reflection_required_by_validation?(reflection)
23
+ end
24
+
25
+ sig { params(reflection: ReflectionType).returns(T::Boolean) }
26
+ def belongs_to_and_non_optional_reflection?(reflection)
27
+ return false unless reflection.belongs_to?
28
+
29
+ optional = if reflection.options.key?(:required)
30
+ !reflection.options[:required]
31
+ else
32
+ reflection.options[:optional]
33
+ end
34
+ return !optional unless optional.nil?
35
+ return true if reflection_required_by_database_constraint?(reflection)
36
+ return true if reflection_required_by_validation?(reflection)
37
+
38
+ # nothing defined, so fall back to the default active record config
39
+ !!reflection.active_record.belongs_to_required_by_default
40
+ end
41
+
42
+ private
43
+
44
+ # check for non-nullable database constraint on the foreign key
45
+ sig { params(reflection: ReflectionType).returns(T::Boolean) }
46
+ def reflection_required_by_database_constraint?(reflection)
47
+ Boba::ActiveRecord::AttributeService.has_non_null_database_constraint?(
48
+ reflection.active_record,
49
+ reflection.foreign_key,
50
+ )
51
+ end
52
+
53
+ # check for presence validator on the foreign key or on the association
54
+ sig { params(reflection: ReflectionType).returns(T::Boolean) }
55
+ def reflection_required_by_validation?(reflection)
56
+ return true if Boba::ActiveRecord::AttributeService.has_unconditional_presence_validator?(
57
+ reflection.active_record,
58
+ reflection.foreign_key,
59
+ )
60
+
61
+ Boba::ActiveRecord::AttributeService.has_unconditional_presence_validator?(
62
+ reflection.active_record,
63
+ reflection.name,
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
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.6"
5
+ VERSION = "0.0.8"
6
6
  end
@@ -5,6 +5,8 @@ require "tapioca/dsl/compilers/active_record_associations"
5
5
 
6
6
  return unless defined?(Tapioca::Dsl::Compilers::ActiveRecordAssociations)
7
7
 
8
+ require "boba/active_record/reflection_service"
9
+
8
10
  module Tapioca
9
11
  module Dsl
10
12
  module Compilers
@@ -188,39 +190,14 @@ module Tapioca
188
190
  association_class = type_for(reflection)
189
191
  return as_nilable_type(association_class) unless association_type_option.persisted?
190
192
 
191
- if has_one_and_required_reflection?(reflection) || belongs_to_and_non_optional_reflection?(reflection)
193
+ if Boba::ActiveRecord::ReflectionService.has_one_and_required_reflection?(reflection)
194
+ association_class
195
+ elsif Boba::ActiveRecord::ReflectionService.belongs_to_and_non_optional_reflection?(reflection)
192
196
  association_class
193
197
  else
194
198
  as_nilable_type(association_class)
195
199
  end
196
200
  end
197
-
198
- # Note - one can do more here. If the association's attribute has an unconditional presence validation, it
199
- # should also be considered required.
200
- sig { params(reflection: ReflectionType).returns(T::Boolean) }
201
- def has_one_and_required_reflection?(reflection)
202
- reflection.has_one? && !!reflection.options[:required]
203
- end
204
-
205
- # Note - one can do more here. If the FK defining the belongs_to association is non-nullable at the DB level, or
206
- # if the association's attribute has an unconditional presence validation, it should also be considered
207
- # non-optional.
208
- sig { params(reflection: ReflectionType).returns(T::Boolean) }
209
- def belongs_to_and_non_optional_reflection?(reflection)
210
- return false unless reflection.belongs_to?
211
-
212
- optional = if reflection.options.key?(:required)
213
- !reflection.options[:required]
214
- else
215
- reflection.options[:optional]
216
- end
217
-
218
- if optional.nil?
219
- !!reflection.active_record.belongs_to_required_by_default
220
- else
221
- !optional
222
- end
223
- end
224
201
  end
225
202
  end
226
203
  end
@@ -5,6 +5,7 @@ require "tapioca/dsl/compilers/active_record_columns"
5
5
 
6
6
  return unless defined?(Tapioca::Dsl::Compilers::ActiveRecordColumns)
7
7
 
8
+ require "boba/active_record/attribute_service"
8
9
  require "tapioca/dsl/helpers/active_record_column_type_helper"
9
10
 
10
11
  module Tapioca
@@ -150,10 +151,16 @@ module Tapioca
150
151
 
151
152
  sig { params(column_name: String).returns([String, String]) }
152
153
  def column_type_for(column_name)
153
- return ["T.untyped", "T.untyped"] if @column_type_option.untyped?
154
+ return ["T.untyped", "T.untyped"] if column_type_option.untyped?
154
155
 
155
- nilable_column = !has_non_null_database_constraint?(column_name) &&
156
- !has_unconditional_presence_validator?(column_name)
156
+ nilable_column = !Boba::ActiveRecord::AttributeService.has_non_null_database_constraint?(
157
+ @constant,
158
+ column_name,
159
+ )
160
+ nilable_column &&= !Boba::ActiveRecord::AttributeService.has_unconditional_presence_validator?(
161
+ @constant,
162
+ column_name,
163
+ )
157
164
 
158
165
  column_type = @constant.attribute_types[column_name]
159
166
  getter_type = column_type_helper.send(
@@ -169,7 +176,8 @@ module Tapioca
169
176
  getter_type
170
177
  end
171
178
 
172
- if @column_type_option.persisted? && (virtual_attribute?(column_name) || !nilable_column)
179
+ virtual_attribute = Boba::ActiveRecord::AttributeService.virtual_attribute?(@constant, column_name)
180
+ if column_type_option.persisted? && (virtual_attribute || !nilable_column)
173
181
  [getter_type, setter_type]
174
182
  else
175
183
  getter_type = as_nilable_type(getter_type) unless column_type_helper.send(
@@ -180,30 +188,6 @@ module Tapioca
180
188
  end
181
189
  end
182
190
 
183
- sig { params(column_name: String).returns(T::Boolean) }
184
- def virtual_attribute?(column_name)
185
- @constant.columns_hash[column_name].nil?
186
- end
187
-
188
- sig { params(column_name: String).returns(T::Boolean) }
189
- def has_non_null_database_constraint?(column_name)
190
- column = @constant.columns_hash[column_name]
191
- return false if column.nil?
192
-
193
- !column.null
194
- end
195
-
196
- sig { params(column_name: String).returns(T::Boolean) }
197
- def has_unconditional_presence_validator?(column_name)
198
- return false unless @constant.respond_to?(:validators_on)
199
-
200
- @constant.validators_on(column_name).any? do |validator|
201
- next false unless validator.is_a?(ActiveRecord::Validations::PresenceValidator)
202
-
203
- !validator.options.key?(:if) && !validator.options.key?(:unless) && !validator.options.key?(:on)
204
- end
205
- end
206
-
207
191
  sig do
208
192
  params(
209
193
  klass: RBI::Scope,
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.6
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Angellist
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-10-12 00:00:00.000000000 Z
11
+ date: 2024-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sorbet-static-and-runtime
@@ -48,6 +48,8 @@ files:
48
48
  - LICENSE
49
49
  - README.md
50
50
  - lib/boba.rb
51
+ - lib/boba/active_record/attribute_service.rb
52
+ - lib/boba/active_record/reflection_service.rb
51
53
  - lib/boba/relations_railtie.rb
52
54
  - lib/boba/version.rb
53
55
  - lib/tapioca/dsl/compilers/active_record_associations_persisted.rb
@@ -59,9 +61,9 @@ licenses:
59
61
  - MIT
60
62
  metadata:
61
63
  bug_tracker_uri: https://github.com/angellist/boba/issues
62
- changelog_uri: https://github.com/angellist/boba/blob/0.0.6/History.md
64
+ changelog_uri: https://github.com/angellist/boba/blob/0.0.8/History.md
63
65
  homepage_uri: https://github.com/angellist/boba
64
- source_code_uri: https://github.com/angellist/boba/tree/0.0.6
66
+ source_code_uri: https://github.com/angellist/boba/tree/0.0.8
65
67
  rubygems_mfa_required: 'true'
66
68
  post_install_message:
67
69
  rdoc_options: []