55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
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
|
# File 'lib/recheck/rails/setup.rb', line 55
def queries model:
model.validators.map do |validator|
validator.attributes.map do |attr|
name = "query_#{validator.kind}_#{attr}"
inspect = validator.inspect.gsub(/(#<[\w:]+):0x[0-9a-f]+ /, '\1 ')
column = model.columns_hash[attr.to_s]
next Placeholder.new inspect:, comment: "Can't query attribute #{attr}, it's not a database column" if column.nil?
type = column.sql_type_metadata.type
if validator.options[:if] || validator.options[:unless]
next Placeholder.new inspect:, comment: "Can't automatically translate this validation's :if or :unless into a query"
end
on = validator.options[:on] || []
on = [:create, :update] if on.empty?
if validator.options[:on] == [:create]
next Placeholder.new inspect:, comment: "Only validates on: :create, so there's nothing to validate for persisted records"
end
warning = if validator.options[:allow_nil] && !column.null
"Warning: model #{model.name} validates #{attr} with :allow_nil but column #{column.name} is NOT NULL, so a 'valid' record can't be saved.\nRemove :allow_nil or make the column nullable."
end
case validator
when ActiveModel::BlockValidator
Placeholder.new inspect:, comment: "Can't automatically translate a Ruby block into a query."
when ActiveModel::Validations::ConfirmationValidator
FunctionPlaceholder.new inspect:, name:, comment: "Coming soon to Recheck beta"
when ActiveModel::Validations::FormatValidator
FunctionPlaceholder.new inspect:, name:, comment: "Coming soon to Recheck beta"
when ActiveModel::Validations::InclusionValidator
FunctionPlaceholder.new inspect:, name:, comment: "Coming soon to Recheck beta"
when ActiveRecord::Validations::AbsenceValidator
FunctionPlaceholder.new inspect:, name:, comment: "Coming soon to Recheck beta"
when ActiveRecord::Validations::AssociatedValidator
FunctionPlaceholder.new inspect:, name:, comment: "Coming soon to Recheck beta"
when ActiveRecord::Validations::LengthValidator
or_clauses = []
if type == :string || type == :text || type == :integer
if validator.options[:is]
or_clauses << %{"LENGTH(`#{column.name}`) != #{validator.options[:is]}"}
end
if validator.options[:minimum] && validator.options[:maximum]
or_clauses << %{"LENGTH(`#{column.name}`) < #{validator.options[:minimum]} and LENGTH(`#{column.name}`) > #{validator.options[:maximum]}"}
elsif validator.options[:minimum]
or_clauses << %{"LENGTH(`#{column.name}`) < #{validator.options[:minimum]}"}
elsif validator.options[:maximum]
or_clauses << %{"LENGTH(`#{column.name}`) > #{validator.options[:maximum]}"}
end
if validator.options[:allow_blank]
or_clauses = or_clauses.map { |clause| clause[..-2] + %( and `#{column.name}` != ''") }
end
elsif type == :boolean
= "Validating length of a boolean is backend-dependent and a strange idea."
else
= "Recheck doesn't know how to handle length on a #{type}, please report."
end
if !validator.options[:allow_nil] && !validator.options[:allow_blank]
or_clauses << "#{column.name}: nil"
end
Query.new inspect:, name:, warning:, comment:, or_clauses:
when ActiveRecord::Validations::NumericalityValidator
FunctionPlaceholder.new inspect:, name:, comment: "Coming soon to Recheck beta"
when ActiveRecord::Validations::PresenceValidator
if validator.options[:allow_blank]
next Placeholder.new inspect:, comment: "Validates presence of #{attr} with :allow_blank, which can never fail."
end
or_clauses = []
if !validator.options[:allow_nil]
or_clauses << "#{column.name}: nil"
end
case type
when :string
or_clauses << %{"TRIM(`#{column.name}`) = ''"}
when :boolean
or_clauses << "#{column.name}: false"
else
= "Recheck doesn't know how to handle presence on a #{type}, please report."
end
Query.new inspect:, name:, warning:, comment:, or_clauses:
when ActiveRecord::Validations::UniquenessValidator
FunctionPlaceholder.new inspect:, name:, comment: "Coming soon to Recheck beta"
end
end
end.flatten
end
|