{"id":20290259,"url":"https://github.com/ksss/type_struct","last_synced_at":"2025-10-23T18:29:28.271Z","repository":{"id":36816149,"uuid":"41123024","full_name":"ksss/type_struct","owner":"ksss","description":null,"archived":false,"fork":false,"pushed_at":"2024-04-14T14:41:18.000Z","size":122,"stargazers_count":108,"open_issues_count":2,"forks_count":7,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-30T02:09:38.241Z","etag":null,"topics":["ruby"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ksss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2015-08-20T23:14:32.000Z","updated_at":"2024-09-13T06:56:22.000Z","dependencies_parsed_at":"2024-04-14T15:36:17.788Z","dependency_job_id":"2c9954d8-8857-4f0d-806c-d4750bc2b8d5","html_url":"https://github.com/ksss/type_struct","commit_stats":{"total_commits":144,"total_committers":2,"mean_commits":72.0,"dds":0.00694444444444442,"last_synced_commit":"86ad4529d087badf997eeb9467fc4dd8baca6c4c"},"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ksss%2Ftype_struct","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ksss%2Ftype_struct/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ksss%2Ftype_struct/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ksss%2Ftype_struct/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ksss","download_url":"https://codeload.github.com/ksss/type_struct/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247430870,"owners_count":20937874,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ruby"],"created_at":"2024-11-14T15:06:44.676Z","updated_at":"2025-10-23T18:29:28.184Z","avatar_url":"https://github.com/ksss.png","language":"Ruby","readme":"# TypeStruct\n\nImitating static typed struct.\n\nAll type is checked by `===` method.\n\n## Motivation\n\n### I don't like Hash\n\n```ruby\np h #=\u003e {color: 'red', width: 120, height: 200}\n```\n\n**No name**\n\nWhat is this a data?\nWe cannot know this name.\n\nWhere is this an instance from?\nHow do we grep doing?\n\n**Bad syntax**\n\n```ruby\nh[:widht] #=\u003e Cannot detect typo\n#=\u003e nil\nh.dig(:widht) #=\u003e ditto\n#=\u003e nil\nh.fetch(:widht) #=\u003e Can detect typo, But too long and cannot know suggestion from did_you_mean gem\n# KeyError: key not found: :widht\n```\n\n**Too freedom**\n\n```ruby\n# Where is from `who` key? Is this expected?\np h #=\u003e {color: 'red', width: 120, height: 200, who: 'are you?'}\n```\n\n### I like Struct\n\n**Grepable Name**\n\n```ruby\nCircle = Struct.new(:color, :width, :height)\ncircle = Circle.new('red', 120, 200)\n```\n\n**Good syntax**\n\n```ruby\ncircle.color\n#=\u003e 'red'\ncircle.widht\n# NoMethodError:\nDid you mean?  width\n               width=\n```\n\n**Strictly members**\n\n```ruby\ncircle.who = \"are you?\"\n# NoMethodError: undefined method `who='\n```\n\n## Evolution\n\n- Can use keyword arguments\n- Add **Type** system\n- Recursive Mapping\n\nThis is the **TypeStruct**.\n\n## Usage\n\n### Check type\n\n```ruby\nSample = TypeStruct.new(\n  str: String,\n  reg: /exp/,\n  num: Integer,\n  any: Object,\n)\n\nsample = Sample.new(\n  str: \"instance of String\",\n  reg: \"match to regexp\",\n  num: 10,\n  any: true,\n)\n\np sample\n#=\u003e #\u003cSample str=\"instance of String\", reg=\"not match to regexp\", num=10, any=true\u003e\n\np sample.to_h\n#=\u003e {:str=\u003e\"instance of String\", :reg=\u003e\"not match to regexp\", :num=\u003e10, :any=\u003etrue}\n\np sample.str\n#=\u003e \"instance of String\"\n\nsample.string #=\u003e NoMethodError\nsample.str = 1 #=\u003e TypeError\n```\n\n### Recursive Mapping\n\nGenerate object from hash by recursive.\n\nLike JSON package of golang and crystal-lang.\n\n```ruby\nPoint = TypeStruct.new(\n  x: Integer,\n  y: Integer,\n)\nColor = TypeStruct.new(\n  code: /\\A#[0-9a-f]{6}\\z/i,\n)\nLine = TypeStruct.new(\n  start: Point,\n  end: Point,\n  color: Color,\n)\n\nhash = JSON.parse(%({\"start\":{\"x\":3,\"y\":10},\"end\":{\"x\":5,\"y\":9},\"color\":{\"code\":\"#CAFE00\"}}))\nline = Line.from_hash(hash)\n\np line\n#=\u003e #\u003cLine start=#\u003cPoint x=3, y=10\u003e, end=#\u003cPoint x=5, y=9\u003e, color=#\u003cColor code=\"#CAFE00\"\u003e\u003e\np line.start.y\n#=\u003e 10\nline.stort\n#=\u003e NoMethodError\n```\n\n## Four special classes\n\n### Union\n\nUnion is an object express class that some classes as one class like crystal `Union`.\n\n`Union#===` check all object with `===` method.\n\n```ruby\nFoo = TypeStruct.new(\n  bar: TypeStruct::Union.new(TrueClass, FalseClass)\n)\np Foo.new(bar: false) #=\u003e #\u003cFoo bar=false\u003e\n```\n\nor add `Class#|` method by refinements\n\n```ruby\nrequire \"type_struct/ext\"\nusing TypeStruct::Union::Ext\nFoo = TypeStruct.new(\n  bar: TrueClass | FalseClass,\n)\n```\n\n### ArrayOf\n\nArrayOf is an object express array type.\n\n`ArrayOf#===` check all item with `===` method.\n\n```ruby\nBar = TypeStruct.new(\n  baz: TypeStruct::ArrayOf.new(Integer),\n)\np Bar.new(baz: [1, 2, 3]) #=\u003e #\u003cBar baz=[1, 2, 3]\u003e\n```\n\n### HashOf\n\n`HashOf#===` check all keys and values with `===` method.\n\n```ruby\nBaz = TypeStruct.new(\n  qux: TypeStruct::HashOf.new(String, TypeStruct::ArrayOf.new(Integer))\n)\np Baz.new(qux: { \"a\" =\u003e [1, 2, 3] }) #=\u003e #\u003cBaz qux={\"a\"=\u003e[1, 2, 3]}\u003e\np Baz.from_hash(qux: { \"a\" =\u003e [1, 2, 3] }) #\u003cBaz qux={\"a\"=\u003e[1, 2, 3]}\u003e\np Baz.new(qux: { :a  =\u003e [1, 2, 3] }) #=\u003e TypeError\np Baz.new(qux: { \"a\" =\u003e [1, 2, nil] }) #=\u003e TypeError\n```\n\n### Interface\n\nInterface is an object for duck typing like golang `interface`.\n\n`Interface#===` check all method using `respond_to?`\n\n```ruby\nFoo = TypeStruct.new(\n  bar: TypeStruct::Interface.new(:read, :write)\n  # or Interface.new(:read, :write) on required 'type_struct/ext'\n)\nFoo.new(bar: $stdin)\nFoo.new(bar: 1) #=\u003e TypeError\n```\n\n### Mix\n\n```ruby\nrequire \"type_struct/ext\"\nusing TypeStruct::Union::Ext\nBaz = TypeStruct.new(\n  qux: ArrayOf(Integer | TrueClass | FalseClass) | NilClass\n)\np Baz.new(qux: [1]) #=\u003e #\u003cAAA::Baz qux=[1]\u003e\np Baz.new(qux: [true, false]) #=\u003e #\u003cAAA::Baz qux=[true, false]\u003e\np Baz.new(qux: nil) #=\u003e #\u003cAAA::Baz qux=nil\u003e\np Baz.new(qux: 1) #=\u003e TypeError\np Baz.from_hash(qux: [1, 2, false, true]) #=\u003e #\u003cA::Baz qux=[1, 2, false, true]\u003e\n```\n\n## Auto Generator\n\n```rb\n$ echo '{\"posts\": [{\"number\":10491,\"name\":\"ksss\"}]}' | ruby -r type_struct/generator/json\nPost = TypeStruct.new(\n  number: Integer,\n  name: String,\n)\nAutoGeneratedStruct = TypeStruct.new(\n  posts: ArrayOf(Post),\n)\n```\n\n## Installation\n\nAdd this line to your application's Gemfile:\n\n```ruby\ngem 'type_struct'\n```\n\nAnd then execute:\n\n    $ bundle\n\nOr install it yourself as:\n\n    $ gem install type_struct\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fksss%2Ftype_struct","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fksss%2Ftype_struct","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fksss%2Ftype_struct/lists"}