Skip to content

Instantly share code, notes, and snippets.

@searls
Last active April 23, 2024 16:07
Show Gist options
  • Save searls/ee2a2aab1d4bdbeeca3345991c1a8d97 to your computer and use it in GitHub Desktop.
Save searls/ee2a2aab1d4bdbeeca3345991c1a8d97 to your computer and use it in GitHub Desktop.
Is this overkill? Is there some other nice way to do this?
class ValidatesDeeply
Failure = Struct.new(:model, :message)
Result = Struct.new(:valid?, :failures)
def validate(model, visited = Set.new)
return Result.new(true) if visited.include?(model)
visited.add(model)
combine_results(Result.new(model.valid?, extract_failures(model)), *validate_associations(model, visited))
end
private
def combine_results(*results)
Result.new(results.all?(&:valid?), results.flat_map(&:failures).compact)
end
def extract_failures(model)
model.errors.full_messages.map { |message| Failure.new(model, message) }
end
SKIP_ASSOCIATED_TABLES = %w[
active_storage_attachments
active_storage_blobs
active_storage_variant_records
].freeze
def validate_associations(model, visited)
model.class.reflect_on_all_associations.reject { |assoc|
assoc.is_a?(ActiveRecord::Reflection::BelongsToReflection) ||
assoc.is_a?(ActiveRecord::Reflection::ThroughReflection) ||
SKIP_ASSOCIATED_TABLES.include?(assoc.table_name)
}.flat_map do |association|
associated_records = model.send(association.name)
validate_association(associated_records, visited)
end
end
def validate_association(associated, visited)
case associated
when ActiveRecord::Base
validate(associated, visited)
when Enumerable
associated.map { |record| validate(record, visited) }
else
Result.new(true)
end
end
end
@zacharydanger
Copy link

So roughly: HighLevelThing -> lots of DependentAssociatedModels's, we don't necessarily care about Serious Validation™ until HighLevelModel flips some published? bit/enum.

There might still be a vanilla Rails approach:

#pseudo-rails code, obvs

class HighLevelThing
  has_many :whatevers
  validates_associated :whatevers, if: :published?
end

class Whatever
  belongs_to :high_level_thing

  # serious validation saved for high level publishing
  validates :whatever, if: -> { high_level_thing.published? }
end

Definitely a matter of taste—I'm sure ValidatesDeeply does the thing you want it to, but 6+ months out I know which code I'd rather have forgotten. 😅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment