Imitating static typed struct.
All type is checked by === method.
p h #=> {color: 'red', width: 120, height: 200}
No name
What is this a data? We cannot know this name.
Where is this an instance from? How do we grep doing?
Bad syntax
h[:widht] #=> Cannot detect typo #=> nil h.dig(:widht) #=> ditto #=> nil h.fetch(:widht) #=> Can detect typo, But too long and cannot know suggestion from did_you_mean gem # KeyError: key not found: :widht
Too freedom
# Where is from `who` key? Is this expected? p h #=> {color: 'red', width: 120, height: 200, who: 'are you?'}
Grepable Name
Circle = Struct.new(:color, :width, :height) circle = Circle.new('red', 120, 200)
Good syntax
circle.color #=> 'red' circle.widht # NoMethodError: Did you mean? width width=
Strictly members
circle.who = "are you?" # NoMethodError: undefined method `who='
- Can use keyword arguments
- Add Type system
- Recursive Mapping
This is the TypeStruct.
Sample = TypeStruct.new( str: String, reg: /exp/, num: Integer, any: Object, ) sample = Sample.new( str: "instance of String", reg: "match to regexp", num: 10, any: true, ) p sample #=> #<Sample str="instance of String", reg="not match to regexp", num=10, any=true> p sample.to_h #=> {:str=>"instance of String", :reg=>"not match to regexp", :num=>10, :any=>true} p sample.str #=> "instance of String" sample.string #=> NoMethodError sample.str = 1 #=> TypeError
Generate object from hash by recursive.
Like JSON package of golang and crystal-lang.
Point = TypeStruct.new( x: Integer, y: Integer, ) Color = TypeStruct.new( code: /\A#[0-9a-f]{6}\z/i, ) Line = TypeStruct.new( start: Point, end: Point, color: Color, ) hash = JSON.parse(%({"start":{"x":3,"y":10},"end":{"x":5,"y":9},"color":{"code":"#CAFE00"}})) line = Line.from_hash(hash) p line #=> #<Line start=#<Point x=3, y=10>, end=#<Point x=5, y=9>, color=#<Color code="#CAFE00">> p line.start.y #=> 10 line.stort #=> NoMethodError
Union is an object express class that some classes as one class like crystal Union.
Union#=== check all object with === method.
Foo = TypeStruct.new( bar: TypeStruct::Union.new(TrueClass, FalseClass) ) p Foo.new(bar: false) #=> #<Foo bar=false>
or add Class#| method by refinements
require "type_struct/ext" using TypeStruct::Union::Ext Foo = TypeStruct.new( bar: TrueClass | FalseClass, )
ArrayOf is an object express array type.
ArrayOf#=== check all item with === method.
Bar = TypeStruct.new( baz: TypeStruct::ArrayOf.new(Integer), ) p Bar.new(baz: [1, 2, 3]) #=> #<Bar baz=[1, 2, 3]>
HashOf#=== check all keys and values with === method.
Baz = TypeStruct.new( qux: TypeStruct::HashOf.new(String, TypeStruct::ArrayOf.new(Integer)) ) p Baz.new(qux: { "a" => [1, 2, 3] }) #=> #<Baz qux={"a"=>[1, 2, 3]}> p Baz.from_hash(qux: { "a" => [1, 2, 3] }) #<Baz qux={"a"=>[1, 2, 3]}> p Baz.new(qux: { :a => [1, 2, 3] }) #=> TypeError p Baz.new(qux: { "a" => [1, 2, nil] }) #=> TypeError
Interface is an object for duck typing like golang interface.
Interface#=== check all method using respond_to?
Foo = TypeStruct.new( bar: TypeStruct::Interface.new(:read, :write) # or Interface.new(:read, :write) on required 'type_struct/ext' ) Foo.new(bar: $stdin) Foo.new(bar: 1) #=> TypeError
require "type_struct/ext" using TypeStruct::Union::Ext Baz = TypeStruct.new( qux: ArrayOf(Integer | TrueClass | FalseClass) | NilClass ) p Baz.new(qux: [1]) #=> #<AAA::Baz qux=[1]> p Baz.new(qux: [true, false]) #=> #<AAA::Baz qux=[true, false]> p Baz.new(qux: nil) #=> #<AAA::Baz qux=nil> p Baz.new(qux: 1) #=> TypeError p Baz.from_hash(qux: [1, 2, false, true]) #=> #<A::Baz qux=[1, 2, false, true]>
$ echo '{"posts": [{"number":10491,"name":"ksss"}]}' | ruby -r type_struct/generator/json Post = TypeStruct.new( number: Integer, name: String, ) AutoGeneratedStruct = TypeStruct.new( posts: ArrayOf(Post), )
Add this line to your application's Gemfile:
gem 'type_struct'
And then execute:
$ bundle
Or install it yourself as:
$ gem install type_struct
The gem is available as open source under the terms of the MIT License.