Class: SorbetRails::ModelPlugins::ActiveRecordAssoc

Inherits:
Base
  • Object
show all
Defined in:
lib/sorbet-rails/model_plugins/active_record_assoc.rb

Constant Summary

Constants inherited from Base

Base::Parameter

Instance Attribute Summary

Attributes inherited from Base

#available_classes, #model_class

Instance Method Summary collapse

Methods included from SorbetRails::ModelUtils

#exists_class_method?, #exists_instance_method?, #model_assoc_proxy_class_name, #model_class, #model_class_name, #model_module_name, #model_relation_class_name, #model_relation_shared_module_name

Constructor Details

#initialize(model_class, available_classes) ⇒ ActiveRecordAssoc

Returns a new instance of ActiveRecordAssoc.



5
6
7
8
# File 'lib/sorbet-rails/model_plugins/active_record_assoc.rb', line 5

def initialize(model_class, available_classes)
  super
  @columns_hash = @model_class.table_exists? ? @model_class.columns_hash : {}
end

Instance Method Details

#assoc_should_be_untyped?(reflection) ⇒ Boolean

Returns:

  • (Boolean)


75
76
77
# File 'lib/sorbet-rails/model_plugins/active_record_assoc.rb', line 75

def assoc_should_be_untyped?(reflection)
  polymorphic_assoc?(reflection) || !@available_classes.include?(reflection.klass.name)
end

#generate(root) ⇒ Object



11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/sorbet-rails/model_plugins/active_record_assoc.rb', line 11

def generate(root)
  return unless @model_class.reflections.length > 0

  assoc_module_name = self.model_module_name("GeneratedAssociationMethods")
  assoc_module_rbi = root.create_module(assoc_module_name)
  assoc_module_rbi.create_extend("T::Sig")

  model_class_rbi = root.create_class(self.model_class_name)
  model_class_rbi.create_include(assoc_module_name)

  @model_class.reflections.sort.each do |assoc_name, reflection|
    reflection.collection? ?
      populate_collection_assoc_getter_setter(assoc_module_rbi, assoc_name, reflection) :
      populate_single_assoc_getter_setter(assoc_module_rbi, assoc_name, reflection)
  end
end

#polymorphic_assoc?(reflection) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
89
90
# File 'lib/sorbet-rails/model_plugins/active_record_assoc.rb', line 86

def polymorphic_assoc?(reflection)
  reflection.through_reflection ?
    polymorphic_assoc?(reflection.source_reflection) :
    reflection.polymorphic?
end

#populate_collection_assoc_getter_setter(assoc_module_rbi, assoc_name, reflection) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/sorbet-rails/model_plugins/active_record_assoc.rb', line 54

def populate_collection_assoc_getter_setter(assoc_module_rbi, assoc_name, reflection)
  # TODO allow people to specify the possible values of polymorphic associations
  assoc_class = assoc_should_be_untyped?(reflection) ? "T.untyped" : "::#{reflection.klass.name}"
  relation_class = relation_should_be_untyped?(reflection) ?
    "ActiveRecord::Associations::CollectionProxy" :
    "#{assoc_class}::ActiveRecord_Associations_CollectionProxy"

  assoc_module_rbi.create_method(
    assoc_name.to_s,
    return_type: relation_class,
  )
  assoc_module_rbi.create_method(
    "#{assoc_name}=",
    parameters: [
      Parameter.new("value", type: "T.any(T::Array[#{assoc_class}], #{relation_class})")
    ],
    return_type: nil,
  )
end

#populate_single_assoc_getter_setter(assoc_module_rbi, assoc_name, reflection) ⇒ Object



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/sorbet-rails/model_plugins/active_record_assoc.rb', line 28

def populate_single_assoc_getter_setter(assoc_module_rbi, assoc_name, reflection)
  # TODO allow people to specify the possible values of polymorphic associations
  assoc_class = assoc_should_be_untyped?(reflection) ? "T.untyped" : "::#{reflection.klass.name}"
  assoc_type = "T.nilable(#{assoc_class})"
  if reflection.belongs_to?
    # if this is a belongs_to connection, we may be able to detect whether
    # this field is required & use a stronger type
    column_def = @columns_hash[reflection.foreign_key.to_s]
    if column_def
      assoc_type = assoc_class if !column_def.null
    end
  end

  assoc_module_rbi.create_method(
    assoc_name.to_s,
    return_type: assoc_type,
  )
  assoc_module_rbi.create_method(
    "#{assoc_name}=",
    parameters: [
      Parameter.new("value", type: assoc_type)
    ],
    return_type: nil,
  )
end

#relation_should_be_untyped?(reflection) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
83
# File 'lib/sorbet-rails/model_plugins/active_record_assoc.rb', line 80

def relation_should_be_untyped?(reflection)
  # only type the relation we'll generate
  assoc_should_be_untyped?(reflection) || !@available_classes.include?(reflection.klass.name)
end