v1.1.0
Strictly Define Interfaces (and more!)
Strict::Interface
With this release, we've introduced Strict::Interface
- a new way to strictly validate a cohesive set of functionality that might have different implementations. Consider, for example, a codebase that offers a Storage
class:
class Storage
def write(key:, contents:)
# write to file
end
def read(key:)
# read from file
end
end
Within your tests, rather than writing to the filesystem you might want to write to an in-memory store. Where you typically might rely on duck typing, you can now define a Strict::Interface
and multiple implementations which conform to that interface. See below for an example:
class Storage
extend Strict::Interface
expose(:write) do
key String
contents String
returns Boolean()
end
expose(:read) do
key String
returns AnyOf(String, nil)
end
end
module Storages
class Memory
def initialize
@storage = {}
end
def write(key:, contents:)
storage[key] = contents
true
end
def read(key:)
storage[key]
end
private
attr_reader :storage
end
end
storage = Storage.new(Storages::Memory.new)
# => #<Storage implementation=#<Storages::Memory>>
storage.write(key: "some/path/to/file.rb", contents: "Hello")
# => true
storage.write(key: "some/path/to/file.rb", contents: {})
# => Strict::MethodCallError
storage.read(key: "some/path/to/file.rb")
# => "Hello"
storage.read(key: "some/path/to/other.rb")
# => nil
module Storages
class Wat
def write(key:)
end
end
end
storage = Storage.new(Storages::Wat.new)
# => Strict::ImplementationDoesNotConformError
Strict Attributes Coercers
You can now easily coerce into objects or values defined with strict attributes. ValueOrObjectClass.coercer
will provide a coercer that will turn a hash-like object into an instance of the class.
class Money
include Strict::Value
attributes do
amount_in_cents Integer
currency AnyOf("USD", "CAD"), default: "USD"
end
end
class Transaction
include Strict::Value
attributes do
from_account String
to_account String
amount Money, coerce: Money.coercer
end
end
Transaction.new(from_account: "1", to_account: "2", amount: { amount_in_cents: 100_00 })
# => #<Transaction from_account="1" to_account="2" amount=#<Money amount_in_cents=100_00 currency="USD">>
What's Changed
- Make some stronger Strict::Method assertions by @kylekthompson in #15
- Add coercers for attributes classes, hashes, and arrays by @kylekthompson in #17
- Add Strict::Interface by @kylekthompson in #19
- Add Strict::Interface examples by @kylekthompson in #20
- v1.1.0 by @kylekthompson in #21
Full Changelog: v1.0.0...v1.1.0