Gem Version

Lab42::BasicConstraints

N.B. All these code examples are verified with the speculate_about gem

A collection of constraints

Context A Quick Overview

Given That we alias Lab42::BasicConstraints to BC

  require "lab42/basic_constraints/alias"

Given

    let :all_basic_constraints do
      %i[
       all_digits_string alphanumeric_string
       bool
       date date_time day
       hour
       int
       lowercase_string
       minute month
       non_negative_int non_negative_number number
       positive_int positive_number
       second string symbol
       uppercase_string
       year
      ]
    end
    let :all_parametrized_constraints do
      %i[ int_range limited_string ]
    end

Then All Basic Constraints can be listed by means of BC.all_constraints


    expect( Set.new(BC.all_constraints) ).to eq(Set.new(all_basic_constraints + all_parametrized_constraints))

And they are implemented of course

  all_basic_constraints.each do | constraint_name |
    expect( BC.from_symbol(constraint_name) ).to be_a(BC::Constraint)
  end
  expect( BC.int_range(min: 0) ).to be_a(BC::Constraint)
  expect( BC.limited_string(max: 10) ).to be_a(BC::Constraint)

So we got the ball, now use it!

The ball, BTW, is a Lab42::Result see lab42_result gem for more info

Given the non_negative_int constraint

    let(:non_neg_int) {BC.non_negative_int}

Then we can just call it

    non_neg_int.(0) in [:ok, nil]
    non_neg_int.(-1) in [error, message]
    expect(error).to eq(BC::ConstraintError)
    expect(message).to eq("-1 is not a legal non_negative_int")

Example: Get a constraint by symbol

    pn = BC.from_symbol(:positive_number)
    pn.(1) in [:ok, nil]

Example: Constraints with and without defaults

    expect {BC.from_symbol(:positive_number).default}
      .to raise_error(Lab42::BasicConstraints::MissingDefaultError, "Constraint positive_number has no predefined default")

    expect( BC.non_negative_int.default ).to eq(0)

Example: Avoiding the Exception, à la Hash#fetch

    expect( BC.from_symbol(:positive_number).default{42} ).to eq(42)

Context Time's a Sticky Wicket

... but as defaults are defined for runtime that shall really help us with that

Given that we have timecop

    require "timecop"
    let(:d) {BC.date}
    let(:error) {Lab42::BasicConstraints::ConstraintError}

Then we can show that the year's default value will not be at compile time but at runtime:

  year = BC.year
  Timecop.freeze(Time.utc(1905)) do
    expect( year.default ).to eq(1905)
  end

Of course a check for legality is performed

Example: Illegal Dates

    d.("2020-00-01") in [raised, message ]
    expect( raised ).to eq(error)
    expect( message ).to eq(%{"2020-00-01" is not a legal date})

Context Equality More Of A Tricky Sticker

And Most Surprisingly

    expect(BC.bool).not_to eq(BC.bool) 

However in many cases the name and the default say a lot about a constraint's behavior

Given

    let(:natural) {BC.non_negative_int}
    let(:def42) {BC.non_negative_int.set_default(42)}

Then we can compare as follows

    expect( natural.name ).to eq(BC.non_negative_int.name)
    expect( natural.default ).to eq(0)
    expect( def42.name ).to eq(BC.non_negative_int.name)
    expect( def42.default ).to eq(42)

Detailed Description of all Constraints

date_and_time_constraints div_constraints string_constraints

LICENSE

Copyright 2020 Robert Dober [email protected]

Apache-2.0 c.f LICENSE