https://github.com/enspirit/finitio-rb
Finitio - In Ruby
https://github.com/enspirit/finitio-rb
Last synced: 14 days ago
JSON representation
Finitio - In Ruby
- Host: GitHub
- URL: https://github.com/enspirit/finitio-rb
- Owner: enspirit
- License: mit
- Created: 2014-02-19T17:23:54.000Z (about 11 years ago)
- Default Branch: master
- Last Pushed: 2025-03-27T12:27:52.000Z (30 days ago)
- Last Synced: 2025-04-03T15:48:32.872Z (23 days ago)
- Language: Ruby
- Homepage: http://www.finitio.io/
- Size: 635 KB
- Stars: 67
- Watchers: 10
- Forks: 4
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
Awesome Lists containing this project
README
[](https://travis-ci.org/blambeau/finitio-rb)
[](https://codeclimate.com/github/blambeau/finitio-rb)
[](https://coveralls.io/r/blambeau/finitio-rb)# Finitio(-rb)
*Finitio* is a language for capturing information structure. Think "JSON/XML
schema" but the right way. For more information about *Finitio* itself, see
[www.finitio.io](http://www.finitio.io)`finitio-rb` is the ruby binding of *Finitio*. It allows defining data schemas
and validating and coercing data against them in an idiomatic ruby way.## Example
```ruby
require 'finitio'
require 'json'# Let load a schema
schema = Finitio.system <<-FIO
@import finitio/data{
name: String( s | s.strip.size > 0 ),
at: DateTime
}
FIO# Let load some JSON document
data = JSON.parse <<-JSON
{ "name": "Finitio", "at": "20142-03-01" }
JSON# And try dressing that data
puts schema.dress(data)
```## ADTs with internal contracts
`finitio-rb` tries to provide an idiomatic binding for ruby developers. In
particular, it uses a simple convention-over-configuration protocol for
information contracts. This protocol is easily described through an example.
The following ADT definition:```ruby
Color = .Color {r: Byte, g: Byte, b: Byte}
```expects the following ruby class:
```ruby
class Color# Constructor & internal representation
def initialize(r, g, b)
@r, @g, @b = r, g, b
end
attr_reader :r, :g, :b# Public dresser for the RGB information contract on the class
def self.rgb(tuple)
new(tuple[:r], tuple[:g], tuple[:b])
end# Public undresser on the instance
def to_rgb
{ r: @r, g: @g, b: @b }
end# ...
end
```## ADTs with external contracts
When the scenario above is not possible or not wanted (would require core
extensions for instance), `finitio-rb` allows defining ADTs with external
contracts. The following ADT definition:```ruby
Color = .Color {r: Byte, g: Byte, b: Byte} .RgbContract
```expected the following ruby module:
```ruby
module RgbContractdef self.dress(tuple)
Color.new(tuple[:r], tuple[:g], tuple[:b])
enddef self.undress(color)
{ r: color.r, g: color.g, b: color.b }
endend
```## Decompose complex system with imports
It is useful to decompose complex systems in many files using the import
feature. The latter works with relative file paths like this:```
# child.fioPosint = .Integer(i | i >= 0)
``````
# parent.fio
@import ./child.fio# Child's types are available inside the system, but not outside it, that
# is, imported types are not themselves exported
Byte = Posint(i | i <= 255 )
``````
@import ./parent.fio# This will work
HalfByte = Byte(i | i <= 128)# But this will not: Posint is not defined
Posint(i | i <= 128)
```See the next section about the standard library if you need to share types
without relying on relative paths.## Standard library
Usual type definitions are already defined for simple data types, forming
Finitio's default system:* Most ruby native (data) classes are already aliased to avoid explicit use of
builtins. In particular, `Integer`, `String`, etc.* A `Boolean` union type also hides the TrueClass and FalseClass distinction.
* Date, Time and DateTime ADTs are also provided that perform common
conversions from JSON strings, through iso8601.This system is best used through Finitio's so-called "standard library", e.g.
```
@import finitio/data# String, Integer, Boolean, etc. are now available in this system
```See `lib/finitio/stdlib/*.fio` for the precise definition of the standard library.
### Contributing to the standard library
Ruby gems may contribute to the standard library by specifying resolve paths.
We suggest the following system file structure inside your gem source code:```
lib
myrubygem
myrubygem.rb
finitio
myrubygem
base.fio
advanced.fio
```Registering the standard library path can then be done as follows:
```
# inside myrubygem.rb
Finitio.stdlib_path(File.expand_path('../../finitio', __FILE__))
```Then, a Finitio schema will have access to the types defined in your extension:
```
@import myrubygem/base
@import myrubygem/advanced
```## About representations
The `Rep` representation function mapping *Finitio* types to ruby classes is
as follows:```ruby
# Any type is represented by Ruby's Object class
Rep(.) = Object# Builtins are represented by the corresponding ruby class
Rep(.Builtin) = Builtin# Sub types are represented by the same representation as the super type
Rep(SuperType( s | ... )) = Rep(SuperType)# Unions are represented by the corresponding classes. The guaranteed result
# class is thus the least common super class (^) of the corresponding
# representations of candidate types
Rep(T1 | ... | Tn) = Rep(T1) ^ ... ^ Rep(Tn)# Sequences are represented through ::Array.
Rep([ElmType]) = Array# Sets are represented through ::Set.
Rep({ElmType}) = Set# Tuples are represented through ruby ::Hash. Attribute names are always
# symbolized
Rep({Ai => Ti}) = Hash Rep(Ti)># Relations are represented through ruby ::Set of ::Hash.
Rep({{Ai => Ti}}) = Set Rep(Ti)>># Abstract data types are represented through the corresponding class when
# specified. ADTs behave as Union types if no class is bound.
Rep(.Builtin ...) = Builtin
```