No description or website provided.
Ruby
Clone or download
Latest commit ff5b0cf Jan 31, 2017
Permalink
Failed to load latest commit information.
lib v0.7.4 Jan 31, 2017
test Expect show file path and lineno Jan 31, 2017
.gitignore first implement Aug 20, 2015
.travis.yml Remove support 2.0.0 Feb 4, 2016
Gemfile first implement Aug 20, 2015
LICENSE.txt first implement Aug 20, 2015
README.md Fix doc Jul 9, 2016
Rakefile Add benchmark Mar 29, 2016
type_struct.gemspec Remove dependency on development Jan 31, 2017

README.md

TypeStruct

Build Status

Imitating static typed struct.

All type is checked by === method.

Motivation

I don't like Hash

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?'}

I like Struct

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='

Evolution

  • Can use keyword arguments
  • Add Type system
  • Recursive Mapping

This is the TypeStruct.

Usage

Check type

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

Recursive Mapping

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

Four special classes

Union

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

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

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

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

Mix

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]>

Auto Generator

$ 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),
)

Installation

Add this line to your application's Gemfile:

gem 'type_struct'

And then execute:

$ bundle

Or install it yourself as:

$ gem install type_struct

License

The gem is available as open source under the terms of the MIT License.