l43_open_object 0.3.0 → 0.4.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 +4 -4
- data/lib/l43/kernel.rb +14 -0
- data/lib/l43/open_object/initializer.rb +6 -3
- data/lib/l43/open_object/version.rb +1 -1
- data/lib/l43/typed_object/behavior.rb +25 -0
- data/lib/l43/typed_object/constants.rb +12 -0
- data/lib/l43/typed_object/description.rb +41 -0
- data/lib/l43/typed_object/impl_helper.rb +64 -0
- data/lib/l43/typed_object/implementation.rb +91 -0
- data/lib/l43/typed_object.rb +15 -0
- metadata +10 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4de3a4790f943a126eec581676c8b3dfcfb19ee6a300baba9bc5d93a7e43d084
|
|
4
|
+
data.tar.gz: 7da96a9783f76cf20571dec4808dcf33e3690e399d20e320cc51e0445bee3860
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 25f70fda2074d8008c758d0614c4448e921212591ec049148b09ad517ca0d1eedf72209e6bc05acdf4f3e81ac99c69d04597bdf07362de314663c0a7c2bad1cf
|
|
7
|
+
data.tar.gz: b8004ec9f68be0978e5b3d036a441c2a845d3550d5cfa0b3bc26467dd8132f5b1940fef68616ecec5b1ab6725e4740fc251cadf7b7a44a7b83ac4bab161f0e22
|
data/lib/l43/kernel.rb
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Kernel
|
|
4
|
+
def import(*path)
|
|
5
|
+
require(File.join(["l43", *path]))
|
|
6
|
+
|
|
7
|
+
segments = path.map do |seg|
|
|
8
|
+
seg.split("_").map(&:capitalize).join
|
|
9
|
+
end.join("::")
|
|
10
|
+
Kernel.const_get("L43::#{segments}")
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@@ -6,16 +6,19 @@ module L43
|
|
|
6
6
|
module Initializer extend self
|
|
7
7
|
|
|
8
8
|
def define_initialize(klass, atts, defaults, blk)
|
|
9
|
-
klass.define_method :initialize do |**kwds|
|
|
9
|
+
klass.define_method :initialize do |**kwds, &b|
|
|
10
10
|
spurious = kwds.keys - atts - defaults.keys
|
|
11
11
|
raise ArgumentError, "spurious keyword parameters: #{spurious.inspect}" unless spurious.empty?
|
|
12
12
|
|
|
13
13
|
values = defaults.merge(kwds)
|
|
14
14
|
values.each do |key, value|
|
|
15
|
-
|
|
15
|
+
if Proc == value
|
|
16
|
+
instance_variable_set("@#{key}", b)
|
|
17
|
+
else
|
|
18
|
+
instance_variable_set("@#{key}", value)
|
|
19
|
+
end
|
|
16
20
|
end
|
|
17
21
|
|
|
18
|
-
|
|
19
22
|
if blk
|
|
20
23
|
more_values = instance_exec(OpenStruct.new(values), &blk)
|
|
21
24
|
if Hash === more_values
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
module L43
|
|
5
|
+
module TypedObject
|
|
6
|
+
module Behavior
|
|
7
|
+
def ==(other)
|
|
8
|
+
return false unless self.class === other
|
|
9
|
+
other.to_h == to_h
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def deconstruct_keys(*) = to_h
|
|
13
|
+
|
|
14
|
+
def merge(**args)
|
|
15
|
+
self.class.new(**to_h.merge(**args))
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def to_ostruct
|
|
20
|
+
OpenStruct.new(to_h)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module L43
|
|
4
|
+
module TypedObject
|
|
5
|
+
None = BasicObject.new
|
|
6
|
+
|
|
7
|
+
MissingAttribute = Class.new(RuntimeError)
|
|
8
|
+
SpuriousAttribute = Class.new(RuntimeError)
|
|
9
|
+
ValueError = Class.new(RuntimeError)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'ostruct'
|
|
4
|
+
require_relative 'implementation'
|
|
5
|
+
module L43
|
|
6
|
+
module TypedObject
|
|
7
|
+
class Description
|
|
8
|
+
|
|
9
|
+
def check_types!
|
|
10
|
+
ImplHelper.check_value_errors!(args: defaults, attributes:)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def implement =
|
|
14
|
+
Implementation.new(klass:, attributes: attributes.freeze, defaults:)
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
attr_reader :allowed, :klass, :required
|
|
18
|
+
def initialize(klass)
|
|
19
|
+
@klass = klass
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def attr(name, default: None, as: None, &blk)
|
|
24
|
+
attributes.update(name => OpenStruct.new(default:, type: as, constraint: blk))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def attributes = @__attributes__ ||= {}
|
|
28
|
+
|
|
29
|
+
def defaults
|
|
30
|
+
@__defaults__ ||= attributes.inject({}) do |defaults, (name, desc)|
|
|
31
|
+
if desc.default == None
|
|
32
|
+
defaults
|
|
33
|
+
else
|
|
34
|
+
defaults.merge(name => desc.default)
|
|
35
|
+
end
|
|
36
|
+
end.freeze
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module L43
|
|
4
|
+
module TypedObject
|
|
5
|
+
module ImplHelper extend self
|
|
6
|
+
def check_for_missing!(actual:, required:)
|
|
7
|
+
missing = required - actual
|
|
8
|
+
return if missing.empty?
|
|
9
|
+
|
|
10
|
+
raise MissingAttribute, "the following attributes do not have a default, but are not provided: #{missing.inspect}"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def check_for_spurious!(actual:, allowed:)
|
|
14
|
+
spurious = actual - allowed
|
|
15
|
+
return if spurious.empty?
|
|
16
|
+
|
|
17
|
+
raise SpuriousAttribute, "the following attributes have not been defined on this class: #{spurious.inspect}"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def check_type(type, value)
|
|
21
|
+
return if None == type
|
|
22
|
+
case type
|
|
23
|
+
when Array
|
|
24
|
+
return true unless type.member?(value)
|
|
25
|
+
else
|
|
26
|
+
return true unless type === value
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def check_value(desc, value)
|
|
31
|
+
return :type if check_type(desc.type, value)
|
|
32
|
+
case desc.constraint
|
|
33
|
+
when nil
|
|
34
|
+
nil
|
|
35
|
+
else
|
|
36
|
+
return :constraint unless desc.constraint.(value)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def check_value_errors!(args:, attributes:)
|
|
41
|
+
errors = _get_value_errors(args:, attributes:)
|
|
42
|
+
return if errors.empty?
|
|
43
|
+
|
|
44
|
+
raise ValueError, "bad values:\n#{errors.join("\n")}"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
def _get_value_errors(args:, attributes:)
|
|
49
|
+
args.map do |name, value|
|
|
50
|
+
desc = attributes[name]
|
|
51
|
+
case check_value(desc, value)
|
|
52
|
+
when :type
|
|
53
|
+
" bad value: #{value.inspect} for attribute #{name}, expected type was: #{desc.type.inspect}"
|
|
54
|
+
when :constraint
|
|
55
|
+
" value: #{value.inspect} violates constraint for attribute #{name}"
|
|
56
|
+
else
|
|
57
|
+
nil
|
|
58
|
+
end
|
|
59
|
+
end.compact
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'behavior'
|
|
4
|
+
require_relative 'impl_helper'
|
|
5
|
+
|
|
6
|
+
module L43
|
|
7
|
+
module TypedObject
|
|
8
|
+
class Implementation
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
attr_reader :attributes, :defaults, :klass
|
|
12
|
+
|
|
13
|
+
def initialize(klass:, attributes:, defaults:)
|
|
14
|
+
@attributes = attributes
|
|
15
|
+
@defaults = defaults
|
|
16
|
+
@klass = klass
|
|
17
|
+
implement
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def allowed = @__allowed__ ||= attributes.map(&:first)
|
|
21
|
+
|
|
22
|
+
def define_behavior
|
|
23
|
+
klass.send :include, Behavior
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def define_initialize
|
|
27
|
+
allowed = self.allowed
|
|
28
|
+
attributes = self.attributes
|
|
29
|
+
defaults = self.defaults
|
|
30
|
+
required = self.required.keys
|
|
31
|
+
|
|
32
|
+
klass.define_method(:initialize) do |**args|
|
|
33
|
+
args = defaults.merge(args)
|
|
34
|
+
actual = args.keys
|
|
35
|
+
ImplHelper.check_for_missing!(required:, actual:)
|
|
36
|
+
ImplHelper.check_for_spurious!(allowed:, actual:)
|
|
37
|
+
ImplHelper.check_value_errors!(args:, attributes:)
|
|
38
|
+
args.each do |name, value|
|
|
39
|
+
instance_variable_set("@#{name}", value)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def define_read_access
|
|
45
|
+
allowed = self.allowed
|
|
46
|
+
klass.module_eval do
|
|
47
|
+
attr_reader(*allowed)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def define_to_h
|
|
52
|
+
attributes = self.attributes.keys
|
|
53
|
+
klass.define_method(:to_h) do
|
|
54
|
+
attributes.inject({}) do |result, att_name|
|
|
55
|
+
result.merge(att_name => instance_variable_get("@#{att_name}"))
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def define_update
|
|
61
|
+
allowed = self.allowed
|
|
62
|
+
attributes = self.attributes
|
|
63
|
+
klass.define_method(:update) do |**args|
|
|
64
|
+
ImplHelper.check_value_errors!(args:, attributes:)
|
|
65
|
+
ImplHelper.check_for_spurious!(allowed:, actual: args.keys)
|
|
66
|
+
args.each do |name, value|
|
|
67
|
+
instance_variable_set("@#{name}", value)
|
|
68
|
+
end
|
|
69
|
+
self
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def implement
|
|
74
|
+
separate_attributes
|
|
75
|
+
define_read_access
|
|
76
|
+
define_to_h
|
|
77
|
+
define_update
|
|
78
|
+
define_behavior
|
|
79
|
+
define_initialize
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def required = @__required__ ||= {}
|
|
83
|
+
|
|
84
|
+
def separate_attributes
|
|
85
|
+
attributes.each { |name, desc| required.update(name => desc) if desc.default == None }
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'typed_object/constants'
|
|
4
|
+
require_relative 'typed_object/description'
|
|
5
|
+
module L43
|
|
6
|
+
module TypedObject
|
|
7
|
+
def def_atts(&blk)
|
|
8
|
+
desc = Description.new(self)
|
|
9
|
+
desc.instance_eval(&blk)
|
|
10
|
+
desc.check_types!
|
|
11
|
+
desc.implement
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: l43_open_object
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Robert Dober
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-11-01 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ostruct
|
|
@@ -37,10 +37,17 @@ extra_rdoc_files: []
|
|
|
37
37
|
files:
|
|
38
38
|
- LICENSE
|
|
39
39
|
- lib/l43.rb
|
|
40
|
+
- lib/l43/kernel.rb
|
|
40
41
|
- lib/l43/open_object.rb
|
|
41
42
|
- lib/l43/open_object/behavior.rb
|
|
42
43
|
- lib/l43/open_object/initializer.rb
|
|
43
44
|
- lib/l43/open_object/version.rb
|
|
45
|
+
- lib/l43/typed_object.rb
|
|
46
|
+
- lib/l43/typed_object/behavior.rb
|
|
47
|
+
- lib/l43/typed_object/constants.rb
|
|
48
|
+
- lib/l43/typed_object/description.rb
|
|
49
|
+
- lib/l43/typed_object/impl_helper.rb
|
|
50
|
+
- lib/l43/typed_object/implementation.rb
|
|
44
51
|
homepage: https://codeberg.org/lab419/l43_open_object
|
|
45
52
|
licenses:
|
|
46
53
|
- AGPL-3.0-or-later
|
|
@@ -59,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
59
66
|
- !ruby/object:Gem::Version
|
|
60
67
|
version: '0'
|
|
61
68
|
requirements: []
|
|
62
|
-
rubygems_version: 4.0.
|
|
69
|
+
rubygems_version: 4.0.20
|
|
63
70
|
specification_version: 4
|
|
64
71
|
summary: Define attributes with potential defaults, get all the goodies...
|
|
65
72
|
test_files: []
|