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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b6d55f44ec50f1cd6ba1dc6488db8cd515a5cef61841a34da88e30032963932c
4
- data.tar.gz: 3f04fe5f15b9e81f9c80b57099d0bbf691eff30be9f32707002175b7c3b82850
3
+ metadata.gz: 4de3a4790f943a126eec581676c8b3dfcfb19ee6a300baba9bc5d93a7e43d084
4
+ data.tar.gz: 7da96a9783f76cf20571dec4808dcf33e3690e399d20e320cc51e0445bee3860
5
5
  SHA512:
6
- metadata.gz: d132576dde02301168436e9633f0a4caa4b61f61d055b7fcdcb52a3e00665d1c4d5ff8dff00366a30f28c5f12fe54ca2b031bab5a35d6b0c8c8dca11a7e70e98
7
- data.tar.gz: 30e854ee3d537492a0ee2e2d5b69bf0f47a0350071e5953a7596be59e411139ea82c2b06b09e59fc7646102651432033cd0c5979a966abfefbfe9366aa3ad271
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
- instance_variable_set("@#{key}", value)
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module L43
4
4
  module OpenObject
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
8
8
  # SPDX-License-Identifier: AGPL-3.0-or-later
@@ -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.3.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-04-27 00:00:00.000000000 Z
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.10
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: []