Skip to content

An early, in-progress of Changesets in Crystal. Inspired by Elixir's Ecto.Changeset.

License

Notifications You must be signed in to change notification settings

faultyserver/change

Repository files navigation

change

An early, in-progress implementation of Changesets in Crystal. Inspired by Elixir's Ecto Changesets.

Changesets are a powerful way of implementing validations and coercions in a composable, non-destructive way. Changes can be applied and validations can be enforced without ever modifying the underlying instance, making it easy to trust that data manipulation is always done correctly and safely.

What works:

  • cast with typecasting of primitive types (nil, booleans, string, ints, floats).
  • validate_* for making assertions about fields (currently only validate_required).
    • valid? method on the changeset to indicate overall validity.
    • add_error to add an error to the changeset and mark it as invalid.
  • get_* to inspect information about the changeset dynamically.
  • Schema definitions (but this is definitely going to change a lot soon).

Initial goals for this implementation include:

  • Compile Performance: minimal, linear/constant-time overhead with macros and generated code with respect to the number of usages.
  • Type safety: safe, automatic casting of data between types from any input type.
  • Extensibility: enabling user-defined types to be added without requiring modifications to the core

Installation

  1. Add the dependency to your shard.yml:

    dependencies:
      changeset:
        github: faultyserver/change
  2. Run shards install

Usage

This library is too early to have a documented API. In the meantime, check example.cr for an example usage. A short sample is also given here:

require "change"

class User
  Change.schema User,
    name : String,
    age : Int32,
    bio : String

  # Standard pattern for working with changesets. Define a static method
  # that performs casts and validations to abstract that from the callsite.
  def self.changeset(instance, changes={} of String => String)
    Changeset.new(instance)
      .cast(changes, [:name, :age, :bio])
      .validate_required(["name", "age"])
  end
end


user = User.new
changeset = User.changeset(user, {
  name: "Jon",
  age: 23,
})

# Changesets abstract validation to a single place, so clients can just query
# their validity before continuing.
changeset.valid? #=> true

# Changesets don't modify the backing instance until explicitly applied.
user #=> #<User @name=nil, @age=nil, @bio=nil>
user = changeset.apply_changes
user #=> #<User @name="Jon, @age=23, @bio=nil>

SQL querying

In addition to changesets, this library also includes an optional SQL querying interface. This lives in src/change/sql and must be included separately from the changeset module. Change::SQL is built on top of Changesets, so using them is required to be able to use this. Part of this is due to Crystal's current lack of support for copying instance vars effectively at compile time, but also to provide a cleaner, more powerful interface with validations, constraints, and more.

See example-sql.cr for an example of what the SQL interface currently looks like.

About

An early, in-progress of Changesets in Crystal. Inspired by Elixir's Ecto.Changeset.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published