l43_open_object 0.2.9 → 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 +23 -6
- data/lib/l43/open_object/version.rb +8 -0
- data/lib/l43/open_object.rb +10 -3
- 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 +26 -4
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
|
|
@@ -1,25 +1,42 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'ostruct'
|
|
3
4
|
module L43
|
|
4
5
|
module OpenObject
|
|
5
6
|
module Initializer extend self
|
|
6
7
|
|
|
7
8
|
def define_initialize(klass, atts, defaults, blk)
|
|
8
|
-
klass.define_method :initialize do |**kwds|
|
|
9
|
-
missing = atts - kwds.keys
|
|
10
|
-
raise ArgumentError, "missing required keyword parameters: #{missing.inspect}" unless missing.empty?
|
|
9
|
+
klass.define_method :initialize do |**kwds, &b|
|
|
11
10
|
spurious = kwds.keys - atts - defaults.keys
|
|
12
11
|
raise ArgumentError, "spurious keyword parameters: #{spurious.inspect}" unless spurious.empty?
|
|
13
12
|
|
|
14
13
|
values = defaults.merge(kwds)
|
|
15
14
|
values.each do |key, value|
|
|
16
|
-
|
|
15
|
+
if Proc == value
|
|
16
|
+
instance_variable_set("@#{key}", b)
|
|
17
|
+
else
|
|
18
|
+
instance_variable_set("@#{key}", value)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if blk
|
|
23
|
+
more_values = instance_exec(OpenStruct.new(values), &blk)
|
|
24
|
+
if Hash === more_values
|
|
25
|
+
more_values.each do |key, value|
|
|
26
|
+
instance_variable_set("@#{key}", value)
|
|
27
|
+
end
|
|
28
|
+
values = values.merge(more_values)
|
|
29
|
+
end
|
|
17
30
|
end
|
|
31
|
+
|
|
32
|
+
missing = atts - values.keys
|
|
33
|
+
raise ArgumentError, "missing required keyword parameters: #{missing.inspect}" unless missing.empty?
|
|
34
|
+
|
|
18
35
|
if respond_to?(:_init)
|
|
19
36
|
_init
|
|
20
37
|
end
|
|
21
|
-
|
|
22
|
-
if
|
|
38
|
+
|
|
39
|
+
if klass.mutable?
|
|
23
40
|
Initializer.define_dynamic_to_h(klass, values.keys)
|
|
24
41
|
else
|
|
25
42
|
Initializer.define_to_h(klass, values.keys)
|
data/lib/l43/open_object.rb
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
require 'l43/open_object/initializer'
|
|
4
|
+
require 'l43/open_object/behavior'
|
|
5
5
|
module L43
|
|
6
6
|
module OpenObject
|
|
7
|
-
VERSION = "0.2.9"
|
|
8
7
|
|
|
9
8
|
def attributes(*atts, **defaults, &blk)
|
|
10
9
|
attr_reader(*atts)
|
|
@@ -31,6 +30,14 @@ module L43
|
|
|
31
30
|
# end
|
|
32
31
|
end
|
|
33
32
|
|
|
33
|
+
def mutable_object!
|
|
34
|
+
instance_variable_set("@__mutable__", true)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def mutable?
|
|
38
|
+
instance_variable_get("@__mutable__") || false
|
|
39
|
+
end
|
|
40
|
+
|
|
34
41
|
def self.def_class(*a, **k, &b)
|
|
35
42
|
oo = self
|
|
36
43
|
Class.new do
|
|
@@ -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,14 +1,28 @@
|
|
|
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-
|
|
11
|
-
dependencies:
|
|
10
|
+
date: 2026-11-01 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ostruct
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.6.3
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.6.3
|
|
12
26
|
description: |-
|
|
13
27
|
When using the class method `attributes` in a class you get:
|
|
14
28
|
- an initialize method with attributes as kwd params and the respective defaults
|
|
@@ -23,9 +37,17 @@ extra_rdoc_files: []
|
|
|
23
37
|
files:
|
|
24
38
|
- LICENSE
|
|
25
39
|
- lib/l43.rb
|
|
40
|
+
- lib/l43/kernel.rb
|
|
26
41
|
- lib/l43/open_object.rb
|
|
27
42
|
- lib/l43/open_object/behavior.rb
|
|
28
43
|
- lib/l43/open_object/initializer.rb
|
|
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
|
|
29
51
|
homepage: https://codeberg.org/lab419/l43_open_object
|
|
30
52
|
licenses:
|
|
31
53
|
- AGPL-3.0-or-later
|
|
@@ -44,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
44
66
|
- !ruby/object:Gem::Version
|
|
45
67
|
version: '0'
|
|
46
68
|
requirements: []
|
|
47
|
-
rubygems_version: 4.0.
|
|
69
|
+
rubygems_version: 4.0.20
|
|
48
70
|
specification_version: 4
|
|
49
71
|
summary: Define attributes with potential defaults, get all the goodies...
|
|
50
72
|
test_files: []
|