scheming 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a71514dd3a8b1f9640a40adf0bbfd928cab9abe7b6981fad8ac0e25a1c7add01
4
+ data.tar.gz: edad4ab18d9c22efebbdc39b9a7c74d451e9a41b9b85892a743bb0dc726301b2
5
+ SHA512:
6
+ metadata.gz: daaff46c5331a1dd747fadfd50701f497225d05104d443dce75c0b8d244445943cea917e5d2ad39a2daf2fbe271ea08edd0930ac351a12dd1ff552c05b6fb590
7
+ data.tar.gz: 28f7756667869ff46d076f74dc89849d65a5fc3609c2c472d85f02cfe16143c74936ac7d16bb2d577650b53df159d9402d34926999f6e8aa3808d721dec2afc9
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,20 @@
1
+ AllCops:
2
+ SuggestExtensions: false
3
+ NewCops: enable
4
+
5
+ Style/StringLiterals:
6
+ Enabled: true
7
+ EnforcedStyle: single_quotes
8
+
9
+ Metrics/BlockLength:
10
+ AllowedMethods: ['define', 'describe', 'it', 'context', 'let']
11
+
12
+ Style/StringLiteralsInInterpolation:
13
+ Enabled: true
14
+ EnforcedStyle: double_quotes
15
+
16
+ Style/ClassAndModuleChildren:
17
+ Enabled: false
18
+
19
+ Layout/LineLength:
20
+ Max: 90
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2024-04-26
4
+
5
+ - Initial release
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in dee_tea_ohh.gemspec
6
+ gemspec
7
+
8
+ gem 'factory_bot', '~> 6.4'
9
+ gem 'pry', '~> 0.14.2'
10
+ gem 'rake', '~> 13.0'
11
+ gem 'rspec', '~> 3.0'
12
+ gem 'rubocop', '~> 1.21'
data/Gemfile.lock ADDED
@@ -0,0 +1,91 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ scheming (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ activesupport (7.1.3.2)
10
+ base64
11
+ bigdecimal
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ connection_pool (>= 2.2.5)
14
+ drb
15
+ i18n (>= 1.6, < 2)
16
+ minitest (>= 5.1)
17
+ mutex_m
18
+ tzinfo (~> 2.0)
19
+ ast (2.4.2)
20
+ base64 (0.2.0)
21
+ bigdecimal (3.1.7)
22
+ coderay (1.1.3)
23
+ concurrent-ruby (1.2.3)
24
+ connection_pool (2.4.1)
25
+ diff-lcs (1.5.1)
26
+ drb (2.2.1)
27
+ factory_bot (6.4.6)
28
+ activesupport (>= 5.0.0)
29
+ i18n (1.14.4)
30
+ concurrent-ruby (~> 1.0)
31
+ json (2.7.2)
32
+ language_server-protocol (3.17.0.3)
33
+ method_source (1.1.0)
34
+ minitest (5.22.3)
35
+ mutex_m (0.2.0)
36
+ parallel (1.24.0)
37
+ parser (3.3.1.0)
38
+ ast (~> 2.4.1)
39
+ racc
40
+ pry (0.14.2)
41
+ coderay (~> 1.1)
42
+ method_source (~> 1.0)
43
+ racc (1.7.3)
44
+ rainbow (3.1.1)
45
+ rake (13.2.1)
46
+ regexp_parser (2.9.0)
47
+ rexml (3.2.6)
48
+ rspec (3.13.0)
49
+ rspec-core (~> 3.13.0)
50
+ rspec-expectations (~> 3.13.0)
51
+ rspec-mocks (~> 3.13.0)
52
+ rspec-core (3.13.0)
53
+ rspec-support (~> 3.13.0)
54
+ rspec-expectations (3.13.0)
55
+ diff-lcs (>= 1.2.0, < 2.0)
56
+ rspec-support (~> 3.13.0)
57
+ rspec-mocks (3.13.0)
58
+ diff-lcs (>= 1.2.0, < 2.0)
59
+ rspec-support (~> 3.13.0)
60
+ rspec-support (3.13.1)
61
+ rubocop (1.63.3)
62
+ json (~> 2.3)
63
+ language_server-protocol (>= 3.17.0)
64
+ parallel (~> 1.10)
65
+ parser (>= 3.3.0.2)
66
+ rainbow (>= 2.2.2, < 4.0)
67
+ regexp_parser (>= 1.8, < 3.0)
68
+ rexml (>= 3.2.5, < 4.0)
69
+ rubocop-ast (>= 1.31.1, < 2.0)
70
+ ruby-progressbar (~> 1.7)
71
+ unicode-display_width (>= 2.4.0, < 3.0)
72
+ rubocop-ast (1.31.2)
73
+ parser (>= 3.3.0.4)
74
+ ruby-progressbar (1.13.0)
75
+ tzinfo (2.0.6)
76
+ concurrent-ruby (~> 1.0)
77
+ unicode-display_width (2.5.0)
78
+
79
+ PLATFORMS
80
+ x86_64-linux
81
+
82
+ DEPENDENCIES
83
+ factory_bot (~> 6.4)
84
+ pry (~> 0.14.2)
85
+ rake (~> 13.0)
86
+ rspec (~> 3.0)
87
+ rubocop (~> 1.21)
88
+ scheming!
89
+
90
+ BUNDLED WITH
91
+ 2.4.5
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2024 Ben Falk
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # Scheming
2
+
3
+ Ergonomically define and work with data in Ruby
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ gem 'scheming'
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Simple Schema
14
+
15
+ Definition:
16
+ ```ruby
17
+ LineItem = Scheming.object do
18
+ attribute :id, Integer
19
+ attribute :name, String
20
+ attribute :taxable, :bool
21
+ attribute :desc, Nullable(String)
22
+ attribute :price, Float
23
+ attribute :type, Enum('entertainment', 'staple')
24
+ end
25
+
26
+ Receipt = Scheming.object do
27
+ attribute :line_items, Array(LineItem)
28
+ attribute :total, Float
29
+ end
30
+ ```
31
+
32
+ Example:
33
+ ```ruby
34
+ Scheming::Schema.json(Receipt)
35
+ # =>
36
+ {
37
+ type: 'object',
38
+ additionalProperties: false,
39
+ required: %i[line_items total],
40
+ properties: {
41
+ line_items: {
42
+ type: 'array',
43
+ items: {
44
+ type: 'object',
45
+ additionalProperties: false,
46
+ required: %i[id name taxable desc price item_type],
47
+ properties: {
48
+ id: { type: 'integer' },
49
+ name: { type: 'string' },
50
+ taxable: { type: 'boolean' },
51
+ desc: {
52
+ oneOf: [
53
+ { type: 'string' },
54
+ { type: 'null' }
55
+ ]
56
+ },
57
+ price: { type: 'numeric' },
58
+ item_type: {
59
+ type: 'string',
60
+ enum: %w[entertainment staple].to_set
61
+ }
62
+ }
63
+ }
64
+ },
65
+ total: { type: 'numeric' }
66
+ }
67
+ }
68
+ ```
69
+
70
+ ## Development
71
+
72
+ ### Getting Started
73
+
74
+ To get going the following commands are helpful:
75
+
76
+ ```bash
77
+ git clone https://github.com/benfalk/scheming.git
78
+ ./scheming/setup
79
+ cd scheming
80
+ bundle exec rake
81
+ ```
82
+
83
+ ### Installing Locally
84
+
85
+ To install this gem onto your local machine:
86
+
87
+ ```bash
88
+ bundle exec rake install
89
+ ```
90
+
91
+ ## Contributing
92
+
93
+ Bug reports and pull requests are welcome
94
+ on GitHub at https://github.com/benfalk/scheming.
95
+
96
+ ## License
97
+
98
+ The gem is available as open source under the terms of
99
+ the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ require 'rubocop/rake_task'
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[spec rubocop]
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = Attribute Collection
4
+ #
5
+ class Scheming::Attribute::List
6
+ class MissingAttribute < Scheming::Error; end
7
+ include Enumerable
8
+
9
+ # @return [Scheming::Attribute::List]
10
+ def self.empty = @empty ||= new([])
11
+
12
+ # @param attributes [Array<Scheming::Attribute>]
13
+ def initialize(attributes = [])
14
+ @attributes = attributes.uniq(&:field_name).freeze
15
+
16
+ # @type [Hash<Symbol, Scheming::Attribute>]
17
+ @lookup =
18
+ @attributes
19
+ .each_with_object({}) do |attr, lookup|
20
+ lookup[attr.field_name] = attr
21
+ end
22
+ .freeze
23
+ freeze
24
+ end
25
+
26
+ # @param key [Symbol]
27
+ # @return [Scheming::Attribute]
28
+ def attr(key)
29
+ @lookup.fetch(key) do |attr_key|
30
+ raise MissingAttribute, <<~MSG.strip!
31
+ Missing Attribute [#{attr_key}]
32
+ Available Fields:
33
+ #{@attributes.map(&:field_name).join("\n ")}
34
+ MSG
35
+ end
36
+ end
37
+ alias [] attr
38
+
39
+ def required = each.select(&:is_required)
40
+
41
+ # @return [Hash<Symbol, Scheming::Attribute>]
42
+ def to_h = @lookup
43
+
44
+ # Interface for Enumerable
45
+ # @yieldparam [Scheming::Attribute]
46
+ def each(&)
47
+ return enum_for(:each) unless block_given?
48
+
49
+ @attributes.each(&)
50
+ end
51
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = Attribute Collection Builder
4
+ #
5
+ class Scheming::Attribute::ListBuilder
6
+ # @param attributes [Array<Scheming::Attribute>]
7
+ def initialize(attributes = [])
8
+ @attributes = attributes
9
+ freeze
10
+ end
11
+
12
+ # @param field_name [Symbol]
13
+ # @param type [Scheming::Type::Base]
14
+ # @return [Scheming::Attribute::ListBuilder]
15
+ def attribute(field_name, type:, is_required: true)
16
+ attr = Scheming::Attribute.new(
17
+ field_name:,
18
+ type:,
19
+ is_required:
20
+ )
21
+ self.class.new(@attributes + [attr])
22
+ end
23
+
24
+ # @return [Scheming::Attribute::List]
25
+ def build = Scheming::Attribute::List.new(@attributes)
26
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = Attribute
4
+ #
5
+ class Scheming::Attribute
6
+ # @return [Symbol]
7
+ attr_reader :field_name
8
+
9
+ # @return [Scheming::Type::Base]
10
+ attr_reader :type
11
+
12
+ # @return [Boolean]
13
+ attr_reader :is_required
14
+
15
+ # @param field_name [Symbol]
16
+ # @param type [Scheming::Type::Base]
17
+ def initialize(
18
+ field_name:,
19
+ type:,
20
+ is_required: true
21
+ )
22
+ @field_name = field_name
23
+ @type = type
24
+ @is_required = is_required
25
+ freeze
26
+ end
27
+
28
+ require_relative 'attribute/list'
29
+ require_relative 'attribute/list_builder'
30
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = Data Builder
4
+ class Scheming::DSL::DataBuilder
5
+ include Scheming::DSL::TypeSpecs
6
+
7
+ def initialize(builder = Scheming::Attribute::ListBuilder.new)
8
+ @builder = builder
9
+ @resolver = Scheming::DSL::TypeResolver
10
+ end
11
+
12
+ # @param field_name [Symbol]
13
+ # @param type_spec [Object]
14
+ # @param null [Boolean]
15
+ # @return [void]
16
+ def attribute(field_name, type_spec)
17
+ type = @resolver.resolve(type_spec)
18
+ @builder = @builder.attribute(field_name, type:)
19
+ nil
20
+ end
21
+
22
+ # @return [Class]
23
+ def build
24
+ list = @builder.build
25
+ dto_type = Scheming::Type::Object.new(list)
26
+
27
+ data = ::Data.define(*list.map(&:field_name))
28
+ data.instance_variable_set(:@dto_type, dto_type)
29
+ data.include(Scheming::DSL::ObjectTypeDef)
30
+ data
31
+ end
32
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = Object Definition Tag
4
+ #
5
+ # This serves as a tag for produced
6
+ # transformation object definitions.
7
+ #
8
+ module Scheming::DSL::ObjectTypeDef
9
+ # @private
10
+ module ClassMethods
11
+ # @return [Scheming::Type::Object]
12
+ def dto_type = @dto_type
13
+
14
+ def inherited(klass)
15
+ super
16
+ klass.extend(ClassMethods)
17
+ klass.instance_variable_set(
18
+ :@dto_type,
19
+ dto_type
20
+ )
21
+ end
22
+
23
+ # @return [Class<Data>]
24
+ def extend_with(&)
25
+ list = Scheming::Attribute::ListBuilder.new(
26
+ dto_type.attributes.to_a
27
+ )
28
+ builder = Scheming::DSL::DataBuilder.new(list)
29
+ builder.instance_exec(&)
30
+ builder.build
31
+ end
32
+ end
33
+ private_constant :ClassMethods
34
+
35
+ def self.included(klass) = klass.extend(ClassMethods)
36
+ end
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = Type Resolver
4
+ module Scheming::DSL::TypeResolver
5
+ module Constants
6
+ STRING = Scheming::Type::String.new.freeze
7
+ FLOAT = Scheming::Type::Float.new.freeze
8
+ INTEGER = Scheming::Type::Integer.new.freeze
9
+ BOOLEAN = Scheming::Type::Boolean.new.freeze
10
+ end
11
+ private_constant :Constants
12
+
13
+ refine Kernel do
14
+ import_methods Scheming::DSL::TypeSpecs
15
+ end
16
+
17
+ refine Symbol do
18
+ def dto_type
19
+ case self
20
+ when :int, :integer then Scheming::Type::Integer.new
21
+ when :str, :string then Scheming::Type::String.new
22
+ when :float then Scheming::Type::Float.new
23
+ when :bool then Scheming::Type::Boolean.new
24
+ end
25
+ end
26
+ end
27
+
28
+ refine Scheming::Type::Base do
29
+ def dto_type = self
30
+ end
31
+
32
+ refine Array do
33
+ def dto_type
34
+ # TODO: Error Handling
35
+ Scheming::Type::Array.new(first.dto_type)
36
+ end
37
+ end
38
+
39
+ refine Scheming::DSL::ObjectTypeDef do
40
+ def dto_type = self.class.dto_type
41
+ end
42
+
43
+ refine ::String.singleton_class do
44
+ def dto_type = Constants::STRING
45
+ end
46
+
47
+ refine ::String do
48
+ def dto_type = Constants::STRING
49
+ end
50
+
51
+ refine ::Float.singleton_class do
52
+ def dto_type = Constants::FLOAT
53
+ end
54
+
55
+ refine ::Float do
56
+ def dto_type = Constants::FLOAT
57
+ end
58
+
59
+ refine ::Integer.singleton_class do
60
+ def dto_type = Constants::INTEGER
61
+ end
62
+
63
+ refine ::Integer do
64
+ def dto_type = Constants::INTEGER
65
+ end
66
+
67
+ refine ::TrueClass do
68
+ def dto_type = Constants::BOOLEAN
69
+ end
70
+
71
+ refine ::FalseClass do
72
+ def dto_type = Constants::BOOLEAN
73
+ end
74
+
75
+ refine ::Set do
76
+ def dto_type
77
+ # TODO: Type checking of all values
78
+ Scheming::Type::Enum.new(
79
+ first.dto_type,
80
+ values: dup.freeze
81
+ )
82
+ end
83
+ end
84
+
85
+ using self
86
+
87
+ module_function
88
+
89
+ def resolve(any) = any.dto_type
90
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = Type Specifications
4
+ #
5
+ # These helpers allow for a more ergonomic
6
+ # resolution of types
7
+ #
8
+ module Scheming::DSL::TypeSpecs
9
+ def Enum(*values) # rubocop:disable Naming/MethodName
10
+ Scheming::DSL::TypeResolver.resolve(values.to_set)
11
+ end
12
+
13
+ def Object(**attributes) # rubocop:disable Naming/MethodName
14
+ attrs = attributes.map do |field_name, type_spec|
15
+ Scheming::Attribute.new(
16
+ field_name:,
17
+ type: Scheming::DSL::TypeResolver.resolve(type_spec),
18
+ is_required: true
19
+ )
20
+ end
21
+ list = Scheming::Attribute::List.new(attrs)
22
+ Scheming::Type::Object.new(list)
23
+ end
24
+
25
+ def Nullable(type_spec) # rubocop:disable Naming/MethodName
26
+ type = Scheming::DSL::TypeResolver.resolve(type_spec)
27
+ Scheming::Type::Nullable.new(type)
28
+ end
29
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = Domain Specific Language
4
+ #
5
+ # Everyone loves magic; I myself enjoy dabling in
6
+ # the dark arts from time to time. Having said that,
7
+ # this magic is best if it's understandable for those
8
+ # who need to know.
9
+ #
10
+ module Scheming::DSL
11
+ require_relative 'dsl/type_specs'
12
+ require_relative 'dsl/data_builder'
13
+ require_relative 'dsl/object_type_def'
14
+ require_relative 'dsl/type_resolver'
15
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scheming::Schema
4
+ # @private
5
+ module JSON
6
+ module Constants
7
+ NULL = { type: 'null' }.freeze
8
+ INTEGER = { type: 'integer' }.freeze
9
+ FLOAT = { type: 'numeric' }.freeze
10
+ STRING = { type: 'string' }.freeze
11
+ BOOLEAN = { type: 'boolean' }.freeze
12
+ end
13
+ private_constant :Constants
14
+
15
+ refine Scheming::Type::Object do
16
+ # @!attribute [r] attributes
17
+ # @return [Scheming::Attribute::List]
18
+
19
+ # @return [Hash]
20
+ def schema
21
+ {
22
+ type: 'object',
23
+ additionalProperties: false,
24
+ required:,
25
+ properties:
26
+ }.freeze
27
+ end
28
+
29
+ private
30
+
31
+ def required
32
+ attributes.required.map!(&:field_name).freeze
33
+ end
34
+
35
+ def properties
36
+ attributes.to_h.transform_values do |attr|
37
+ attr.type.schema
38
+ end.freeze
39
+ end
40
+ end
41
+
42
+ refine Scheming::Type::Nullable do
43
+ # @!attribute [r] type
44
+ # @return [Scheming::Type::Base]
45
+
46
+ # @return [Hash]
47
+ def schema
48
+ {
49
+ oneOf: [
50
+ type.schema,
51
+ Constants::NULL
52
+ ].freeze
53
+ }.freeze
54
+ end
55
+ end
56
+
57
+ refine Scheming::Type::Array do
58
+ # @!attribute [r] type
59
+ # @return [Scheming::Type::Base]
60
+
61
+ def schema
62
+ {
63
+ type: 'array',
64
+ items: type.schema
65
+ }.freeze
66
+ end
67
+ end
68
+
69
+ refine Scheming::Type::Enum do
70
+ def schema
71
+ type.schema.merge(enum: values).freeze
72
+ end
73
+ end
74
+
75
+ refine Scheming::Type::String do
76
+ # @return [Hash]
77
+ def schema = Constants::STRING
78
+ end
79
+
80
+ refine Scheming::Type::Integer do
81
+ # @return [Hash]
82
+ def schema = Constants::INTEGER
83
+ end
84
+
85
+ refine Scheming::Type::Float do
86
+ # @return [Hash]
87
+ def schema = Constants::FLOAT
88
+ end
89
+
90
+ refine Scheming::Type::Boolean do
91
+ # @return [Hash]
92
+ def schema = Constants::BOOLEAN
93
+ end
94
+
95
+ using self
96
+
97
+ module_function
98
+
99
+ # @param type [Scheming::Type::Base]
100
+ # @return [Hash]
101
+ def schema(type) = type.schema
102
+ end
103
+ private_constant :JSON
104
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scheming
4
+ # = Schema
5
+ #
6
+ # Responsible for producing different schema
7
+ # formats which describe the data type provided
8
+ # to it.
9
+ #
10
+ module Schema
11
+ require_relative 'schema/json'
12
+
13
+ module_function
14
+
15
+ # @param raw_type [Scheming::Type::Base]
16
+ # @return [Hash]
17
+ def json(raw_type)
18
+ type = Scheming::DSL::TypeResolver.resolve(raw_type)
19
+ JSON.schema(type)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # = Type
4
+ #
5
+ # Everything needed to describe and work
6
+ # with defining types to encode to.
7
+ #
8
+ module Scheming::Type
9
+ # = Base Type Definition
10
+ #
11
+ # Any and all shared functionality comes from
12
+ # this base type definition.
13
+ #
14
+ Base = Class.new
15
+
16
+ # = Object Type Definition
17
+ #
18
+ # Holds any number of named fields and the
19
+ # types that they hold
20
+ #
21
+ class Object < Base
22
+ # @return [Scheming::Attribute::List]
23
+ attr_reader :attributes
24
+
25
+ # @param attributes [Scheming::Attribute::List]
26
+ def initialize(attributes = Scheming::Attribute::List.empty)
27
+ super()
28
+ @attributes = attributes
29
+ freeze
30
+ end
31
+ end
32
+
33
+ # = Nullable Type Definition
34
+ #
35
+ # Type wrapper that describes a type can be either
36
+ # the type provided OR it may be Null
37
+ #
38
+ class Nullable < Base
39
+ # @return [Scheming::Type::Base]
40
+ attr_reader :type
41
+
42
+ # @param type [Scheming::Type::Base]
43
+ def initialize(type)
44
+ super()
45
+ @type = type
46
+ freeze
47
+ end
48
+ end
49
+
50
+ # = Enumeration Type Definition
51
+ #
52
+ # The wrapper that describes a type and holds
53
+ # a discrete set of values of that type.
54
+ #
55
+ class Enum < Base
56
+ # @return [Scheming::Type::Base]
57
+ attr_reader :type
58
+
59
+ # @return [Set<Object>]
60
+ attr_reader :values
61
+
62
+ # @param type [Scheming::Type::Base]
63
+ # @param values [Set<Object>]
64
+ def initialize(type, values:)
65
+ super()
66
+ @type = type
67
+ @values = values
68
+ freeze
69
+ end
70
+ end
71
+
72
+ # = Array Type Definition
73
+ #
74
+ # Type wrapper which describes an array of zero
75
+ # or more of the provided type.
76
+ #
77
+ class Array < Base
78
+ # @return [Scheming::Type::Base]
79
+ attr_reader :type
80
+
81
+ # @param type [Scheming::Type::Base]
82
+ def initialize(type)
83
+ super()
84
+ @type = type
85
+ freeze
86
+ end
87
+ end
88
+
89
+ # = Integer Type Definition
90
+ class Integer < Base; end
91
+
92
+ # = Float Type Definition
93
+ class Float < Base; end
94
+
95
+ # = String Type Definition
96
+ class String < Base; end
97
+
98
+ # = Boolean Type Definition
99
+ class Boolean < Base; end
100
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Scheming
4
+ VERSION = '0.1.0'
5
+ end
data/lib/scheming.rb ADDED
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'scheming/version'
4
+
5
+ # = Scheming
6
+ #
7
+ module Scheming
8
+ class Error < StandardError; end
9
+
10
+ require_relative 'scheming/attribute'
11
+ require_relative 'scheming/type'
12
+ require_relative 'scheming/schema'
13
+ require_relative 'scheming/dsl'
14
+
15
+ # @return [Class]
16
+ def self.object(&)
17
+ builder = Scheming::DSL::DataBuilder.new
18
+ builder.instance_exec(&)
19
+ builder.build
20
+ end
21
+ end
data/sig/scheming.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Scheming
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,66 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scheming
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ben Falk
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2024-05-01 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Ergonomic Data Design for the Masses
14
+ email:
15
+ - benjamin.falk@yahoo.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - ".rspec"
21
+ - ".rubocop.yml"
22
+ - CHANGELOG.md
23
+ - Gemfile
24
+ - Gemfile.lock
25
+ - LICENSE.txt
26
+ - README.md
27
+ - Rakefile
28
+ - lib/scheming.rb
29
+ - lib/scheming/attribute.rb
30
+ - lib/scheming/attribute/list.rb
31
+ - lib/scheming/attribute/list_builder.rb
32
+ - lib/scheming/dsl.rb
33
+ - lib/scheming/dsl/data_builder.rb
34
+ - lib/scheming/dsl/object_type_def.rb
35
+ - lib/scheming/dsl/type_resolver.rb
36
+ - lib/scheming/dsl/type_specs.rb
37
+ - lib/scheming/schema.rb
38
+ - lib/scheming/schema/json.rb
39
+ - lib/scheming/type.rb
40
+ - lib/scheming/version.rb
41
+ - sig/scheming.rbs
42
+ homepage:
43
+ licenses:
44
+ - MIT
45
+ metadata:
46
+ rubygems_mfa_required: 'true'
47
+ post_install_message:
48
+ rdoc_options: []
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '3.2'
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ requirements: []
62
+ rubygems_version: 3.4.5
63
+ signing_key:
64
+ specification_version: 4
65
+ summary: Designing Data
66
+ test_files: []