Module: SorbetRails::ModelUtils

Extended by:
T::Helpers, T::Sig
Includes:
ModelColumnUtils
Included in:
SorbetRails::ModelPlugins::Base, ModelRbiFormatter
Defined in:
lib/sorbet-rails/model_utils.rb

Instance Method Summary collapse

Methods included from ModelColumnUtils

#active_record_type_to_sorbet_type, #attribute_has_unconditional_presence_validation?, #model_class, #nilable_column?, #time_zone_aware_column?, #type_for_column_def

Instance Method Details

#add_relation_query_method(root, method_name, parameters: nil, builtin_query_method: false) ⇒ Object



95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/sorbet-rails/model_utils.rb', line 95

def add_relation_query_method(root, method_name, parameters: nil, builtin_query_method: false)
  # a relation querying method will be available on
  # - model (as a class method)
  # - activerecord relation
  # - asocciation collection proxy
  # - association relation
  # in case (1) and (2), it returns a Model::ActiveRecord_Relation
  # in case (3) and (4), it returns a Model::ActiveRecord_AssociationRelation

  # 'unscoped' is a special case where it always returns a ActiveRecord_Relation
  assoc_return_value = method_name == 'unscoped' ? self.model_relation_class_name : self.model_assoc_relation_class_name

  # We can put methods onto modules which are extended/included by the model
  # and relation classes which reduces the RBI footprint for an individual
  # model. However, in Rails 5 query methods that come from scopes or enums
  # get overridden in hidden-definitions so we need to explicitly define them
  # on the model and relation classes.
  if builtin_query_method
    relation_module_rbi = root.create_module(self.model_query_methods_returning_relation_module_name)
    relation_module_rbi.create_method(
      method_name,
      parameters: parameters,
      return_type: self.model_relation_class_name,
    )

    assoc_relation_module_rbi = root.create_module(self.model_query_methods_returning_assoc_relation_module_name)
    assoc_relation_module_rbi.create_method(
      method_name,
      parameters: parameters,
      return_type: assoc_return_value,
    )
  else
    # force generating these methods because sorbet's hidden-definitions generate & override them
    model_class_rbi = root.create_class(self.model_class_name)
    model_class_rbi.create_method(
      method_name,
      parameters: parameters,
      return_type: self.model_relation_class_name,
      class_method: true,
    )

    model_relation_rbi = root.create_class(self.model_relation_class_name)
    model_relation_rbi.create_method(
      method_name,
      parameters: parameters,
      return_type: self.model_relation_class_name,
    )

    model_assoc_relation_rbi = root.create_class(self.model_assoc_relation_class_name)
    model_assoc_relation_rbi.create_method(
      method_name,
      parameters: parameters,
      return_type: assoc_return_value,
    )

    collection_proxy_rbi = root.create_class(self.model_assoc_proxy_class_name)
    collection_proxy_rbi.create_method(
      method_name,
      parameters: parameters,
      return_type: assoc_return_value,
    )
  end
end

#exists_class_method?(method_name) ⇒ Boolean

Returns:

  • (Boolean)


80
81
82
# File 'lib/sorbet-rails/model_utils.rb', line 80

def exists_class_method?(method_name)
  model_class.respond_to?(method_name)
end

#exists_instance_method?(method_name) ⇒ Boolean

Returns:

  • (Boolean)


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

def exists_instance_method?(method_name)
  model_class.method_defined?(method_name)
end

#habtm_class?Boolean

Returns:

  • (Boolean)


17
18
19
20
21
# File 'lib/sorbet-rails/model_utils.rb', line 17

def habtm_class?
  # checking the class name seems to be the cleanest way to figure this out, see:
  # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb#L54
  T.must(model_class.name).start_with?('HABTM_')
end

#model_assoc_proxy_class_nameObject



34
35
36
# File 'lib/sorbet-rails/model_utils.rb', line 34

def model_assoc_proxy_class_name
  "#{model_class_name}::ActiveRecord_Associations_CollectionProxy"
end

#model_assoc_relation_class_nameObject



39
40
41
# File 'lib/sorbet-rails/model_utils.rb', line 39

def model_assoc_relation_class_name
  "#{model_class_name}::ActiveRecord_AssociationRelation"
end

#model_class_nameObject



24
25
26
# File 'lib/sorbet-rails/model_utils.rb', line 24

def model_class_name
  model_class.to_s
end

#model_module_name(module_name) ⇒ Object



70
71
72
# File 'lib/sorbet-rails/model_utils.rb', line 70

def model_module_name(module_name)
  "#{model_class_name}::#{module_name}"
end

#model_query_methods_returning_assoc_relation_module_nameObject



49
50
51
# File 'lib/sorbet-rails/model_utils.rb', line 49

def model_query_methods_returning_assoc_relation_module_name
  "#{model_class_name}::QueryMethodsReturningAssociationRelation"
end

#model_query_methods_returning_relation_module_nameObject



44
45
46
# File 'lib/sorbet-rails/model_utils.rb', line 44

def model_query_methods_returning_relation_module_name
  "#{model_class_name}::QueryMethodsReturningRelation"
end

#model_relation_class_nameObject



29
30
31
# File 'lib/sorbet-rails/model_utils.rb', line 29

def model_relation_class_name
  "#{model_class_name}::ActiveRecord_Relation"
end

#model_relation_type_aliasObject



54
55
56
57
58
59
60
61
62
# File 'lib/sorbet-rails/model_utils.rb', line 54

def model_relation_type_alias
  types = [
    self.model_relation_class_name,
    self.model_assoc_proxy_class_name,
    self.model_assoc_relation_class_name
  ].join(', ')

  "T.any(#{types})"
end

#model_relation_type_class_nameObject



65
66
67
# File 'lib/sorbet-rails/model_utils.rb', line 65

def model_relation_type_class_name
  'RelationType'
end