0.02
No release in over a year
A small wrapper around ActiveModel Validations.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

>= 12.3.3
>= 3.9.0
>= 1.80.0
>= 0.0.0

Runtime

>= 3.2.21
 Project Readme

Gem Version

ValidatedObject

Self-validating Plain Old Ruby Objects using Rails validations.

Create Ruby objects that validate themselves on instantiation, with clear error messages and flexible type checking including union types.

Result: Invalid objects can't be instantiated. Illegal states are unrepresentable.

class Person < ValidatedObject::Base
  validates_attr :name, presence: true
  validates_attr :email, format: { with: URI::MailTo::EMAIL_REGEXP }
end

Person.new(name: 'Alice', email: 'alice@example.com')  # ✓ Valid
Person.new(name: '', email: 'invalid')  # ✗ ArgumentError: "Name can't be blank; Email is invalid"

Key Features

  • Union Types: union(String, Integer) for flexible type validation
  • Array Element Validation: type: [String] ensures arrays contain specific types
  • Clear Error Messages: Descriptive validation failures for debugging
  • Rails Validations: Full ActiveModel::Validations support
  • Immutable Objects: Read-only attributes with validation

Perfect for data imports, API boundaries, and structured data generation.

Basic Usage

Simple Validation

class Dog < ValidatedObject::Base
  validates_attr :name, presence: true
  validates_attr :age, type: Integer, allow_nil: true
end

spot = Dog.new(name: 'Spot', age: 3)
spot.valid? # => true

Type Validation

class Document < ValidatedObject::Base
  validates_attr :title, type: String
  validates_attr :published_at, type: Date, allow_nil: true
  validates_attr :active, type: Boolean
end

The Boolean type accepts true or false values.

Union Types

Union types allow attributes to accept multiple possible types:

Basic Union Types

class Article < ValidatedObject::Base
  # ID can be either a String or Integer
  validates_attr :id, type: union(String, Integer)
  
  # Status can be specific symbol values
  validates_attr :status, type: union(:draft, :published, :archived)
end

article = Article.new(id: "abc123", status: :published)  # ✓ String ID
article = Article.new(id: 42, status: :draft)            # ✓ Integer ID
Article.new(id: 3.14, status: :invalid)                  # ✗ ArgumentError

Mixed Type and Array Unions

class Post < ValidatedObject::Base
  # Author can be a Person object or Organization object
  validates_attr :author, type: union(Person, Organization)
  
  # Tags can be a single string or array of strings
  validates_attr :tags, type: union(String, [String])
  
  # Categories supports multiple formats
  validates_attr :categories, type: union(String, [String], [Category])
end

# All of these are valid:
Post.new(author: person, tags: "ruby")
Post.new(author: org, tags: ["ruby", "rails"])  
Post.new(author: person, categories: [category1, category2])

Schema.org Example

Union types are perfect for Schema.org structured data:

class Organization < ValidatedObject::Base
  # Address can be text or structured PostalAddress
  validates_attr :address, type: union(String, PostalAddress)
  
  # Founder can be Person or Organization
  validates_attr :founder, type: union(Person, Organization, [Person], [Organization])
  
  # Logo can be URL string or ImageObject  
  validates_attr :logo, type: union(String, ImageObject)
end

Array Element Validation

Validate that arrays contain specific types:

class Playlist < ValidatedObject::Base
  validates_attr :songs, type: [Song]           # Array of Song objects
  validates_attr :genres, type: [String]        # Array of strings
  validates_attr :ratings, type: [Integer]      # Array of integers
end

playlist = Playlist.new(
  songs: [song1, song2],
  genres: ["rock", "jazz"],  
  ratings: [4, 5, 3]
)

Alternative Syntax

You can also use the standard Rails validates method:

class Dog < ValidatedObject::Base
  attr_reader :name, :birthday

  validates :name, presence: true
  validates :birthday, type: union(Date, DateTime), allow_nil: true
end

Error Messages

ValidatedObject provides clear, actionable error messages:

doc = Document.new(title: 123, status: :invalid)
# => ArgumentError: Title is a Integer, not a String; Status is a Symbol, not one of :draft, :published, :archived

post = Post.new(tags: [123, "valid"])  
# => ArgumentError: Tags is a Array, not one of String, Array of String

Use Cases

Data Import Validation

# Import CSV with validation
valid_records = []
CSV.foreach('data.csv', headers: true) do |row|
  begin
    valid_records << Person.new(row.to_h)
  rescue ArgumentError => e
    logger.warn "Invalid row: #{e.message}"
  end
end

API Response Objects

class ApiResponse < ValidatedObject::Base
  validates_attr :data, type: union(Hash, [Hash])
  validates_attr :status, type: union(:success, :error)
  validates_attr :message, type: String, allow_nil: true
end

Schema.org Structured Data

The Schema.org gem uses ValidatedObject for type-safe structured data generation.

Installation

Add to your Gemfile:

gem 'validated_object'

Then run:

bundle install

Development

After checking out the repo:

bin/setup          # Install dependencies
bundle exec rspec  # Run tests  
bin/console        # Interactive prompt

Contributing

Bug reports and pull requests welcome on GitHub.

License

Available as open source under the MIT License.