Class: Finitio::AdType
- Defined in:
- lib/finitio/type/ad_type.rb,
lib/finitio/generation/ad_type.rb,
lib/finitio/json_schema/ad_type.rb
Overview
The Abtract/Algebraic Data Type is a generator to build user-defined information types in a more abstract way than using sub types. For instance, a Color could be defined as follows:
Color := <Rgb> {r: Byte, g: Byte, b: Byte},
<Hex> String( s | s =~ /^#[0-9a-f]{6}$/i )
Such a declaration does not define a new type by subtyping but instead declares so-called possible representations for the color. Here, two possible representations are defined: one is a rgb triple through a Tuple type, the other is an hexadecimal notation through a subset of String.
Given an AdType, Finitio requires special dress/undress function pairs to encode/decode from the type to the concrete representations and vice versa. This pair of functions is called the “information contract”.
This class allows capturing such information types. For instance,
# This is the concrete representation of Finitio's Color, through a
# usual Ruby ADT (we call it ColorImpl here to avoid confusion, but in
# practice one would simply call it Color).
class ColorImpl
# ... your usual color implementation
end
# The RGB info type: {r: Byte, g: Byte, b: Byte}
rgb_infotype = TupleType.new(...)
# The RGB contract converter, an object that responds to `call` to
# convert from a valid Hash[r: Fixnum, ...] to a ColorImpl instance.
rgb_contract = ...
AdType.new(ColorImpl, rgb: [rgb_infotype, rgb_contract], hex: ...)
The ruby ADT class is of course used as concrete representation of the AdType, that is,
R(Color) = ColorImpl
Accordingly, the ‘dress` transformation function has the following signature:
dress :: Alpha -> Color throws TypeError
dress :: Object -> ColorImpl throws TypeError
Constant Summary
Constants included from Metadata
Instance Attribute Summary collapse
-
#contracts ⇒ Object
readonly
Returns the value of attribute contracts.
-
#ruby_type ⇒ Object
(also: #representator)
Returns the value of attribute ruby_type.
Instance Method Summary collapse
- #==(other) ⇒ Object (also: #eql?)
- #[](name) ⇒ Object
- #contract_names ⇒ Object
- #default_name ⇒ Object
- #dress(value, handler = DressHelper.new) ⇒ Object
- #generate_data(generator, world = nil) ⇒ Object
- #hash ⇒ Object
- #include?(value) ⇒ Boolean
-
#initialize(ruby_type, contracts, name = nil, metadata = nil) ⇒ AdType
constructor
A new instance of AdType.
- #resolve_proxies(system) ⇒ Object
- #to_json_schema(*args, &bl) ⇒ Object
- #unconstrained ⇒ Object
Methods inherited from Type
#anonymous?, #name, #name=, #named?, #suppremum, #to_s
Methods included from Metadata
#metadata, #metadata=, #metadata?
Constructor Details
#initialize(ruby_type, contracts, name = nil, metadata = nil) ⇒ AdType
Returns a new instance of AdType.
51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/finitio/type/ad_type.rb', line 51 def initialize(ruby_type, contracts, name = nil, = nil) unless ruby_type.nil? or ruby_type.is_a?(Module) raise ArgumentError, "Module expected, got `#{ruby_type}`" end unless contracts.is_a?(Array) && contracts.all?{|c| c.is_a?(Contract) } raise ArgumentError, "[Contract] expected, got `#{contracts}`" end super(name, ) @ruby_type = ruby_type @contracts = contracts.freeze end |
Instance Attribute Details
#contracts ⇒ Object (readonly)
Returns the value of attribute contracts.
64 65 66 |
# File 'lib/finitio/type/ad_type.rb', line 64 def contracts @contracts end |
#ruby_type ⇒ Object Also known as: representator
Returns the value of attribute ruby_type.
64 65 66 |
# File 'lib/finitio/type/ad_type.rb', line 64 def ruby_type @ruby_type end |
Instance Method Details
#==(other) ⇒ Object Also known as: eql?
138 139 140 141 142 143 144 |
# File 'lib/finitio/type/ad_type.rb', line 138 def ==(other) super || ( other.is_a?(AdType) && ruby_type == other.ruby_type && contracts == other.contracts ) end |
#[](name) ⇒ Object
75 76 77 |
# File 'lib/finitio/type/ad_type.rb', line 75 def [](name) contracts.find{|c| c.name == name } end |
#contract_names ⇒ Object
79 80 81 |
# File 'lib/finitio/type/ad_type.rb', line 79 def contract_names contracts.map(&:name) end |
#default_name ⇒ Object
83 84 85 |
# File 'lib/finitio/type/ad_type.rb', line 83 def default_name (ruby_type && ruby_type.name.to_s) || "Anonymous" end |
#dress(value, handler = DressHelper.new) ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/finitio/type/ad_type.rb', line 91 def dress(value, handler = DressHelper.new) # Up should be idempotent with respect to the ADT return value if ruby_type and value.is_a?(ruby_type) # Dressed value and first exception dressed, error = nil, nil # Try each contract in turn. Do nothing on TypeError as # the next candidate could be the good one! Return the # first successfully dressed. contracts.each do |contract| # First make the dress transformation on the information type success, dressed = handler.just_try do contract.infotype.dress(value, handler) end # Save very first error on failure unless success error ||= dressed next end # Seems nice, just try to get one stage higher now success, dressed = handler.just_try(StandardError) do contract.dresser.call(dressed) end if success if ruby_type && !dressed.is_a?(ruby_type) raise "Invalid IC dresser (#{contract.dresser}):"\ " #{ruby_type} expected, got #{dressed.class}" end return dressed else error ||= dressed end end # No one succeeded, just fail handler.failed!(self, value, error) end |
#generate_data(generator, world = nil) ⇒ Object
4 5 6 7 |
# File 'lib/finitio/generation/ad_type.rb', line 4 def generate_data(generator, world = nil) infotype = generator.flip_one_out_of(contracts).infotype generator.call(infotype, world) end |
#hash ⇒ Object
134 135 136 |
# File 'lib/finitio/type/ad_type.rb', line 134 def hash ruby_type.hash ^ contracts.hash end |
#include?(value) ⇒ Boolean
87 88 89 |
# File 'lib/finitio/type/ad_type.rb', line 87 def include?(value) ruby_type === value end |
#resolve_proxies(system) ⇒ Object
147 148 149 |
# File 'lib/finitio/type/ad_type.rb', line 147 def resolve_proxies(system) AdType.new(ruby_type, contracts.map{|t| t.resolve_proxies(system)}, name, ) end |
#to_json_schema(*args, &bl) ⇒ Object
4 5 6 7 8 |
# File 'lib/finitio/json_schema/ad_type.rb', line 4 def to_json_schema(*args, &bl) { anyOf: contracts.map{|c| c.infotype.to_json_schema(*args, &bl) } } end |