object_forge 0.3.0 → 0.4.1
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/README.md +139 -60
- data/lib/object_forge/crucible.rb +50 -13
- data/lib/object_forge/forge.rb +112 -41
- data/lib/object_forge/forge_dsl.rb +48 -39
- data/lib/object_forge/forgeyard.rb +13 -12
- data/lib/object_forge/molds/hash_mold.rb +4 -4
- data/lib/object_forge/molds/keywords_mold.rb +5 -5
- data/lib/object_forge/molds/single_argument_mold.rb +5 -5
- data/lib/object_forge/molds/struct_mold.rb +17 -13
- data/lib/object_forge/molds/wrapped_mold.rb +2 -2
- data/lib/object_forge/molds.rb +78 -25
- data/lib/object_forge/sequence.rb +2 -2
- data/lib/object_forge/version.rb +1 -1
- data/lib/object_forge.rb +96 -34
- data/sig/object_forge/molds.rbs +18 -16
- data/sig/object_forge.rbs +83 -41
- metadata +16 -11
data/lib/object_forge/molds.rb
CHANGED
|
@@ -4,42 +4,47 @@ module ObjectForge
|
|
|
4
4
|
# This module provides a collection of predefined molds to be used in common cases.
|
|
5
5
|
#
|
|
6
6
|
# Mold is an object that knows how to take a hash of attributes
|
|
7
|
-
# and create an object from them. Molds are
|
|
7
|
+
# and create an object from them. Molds are +call+able objects
|
|
8
8
|
# responsible for actually building objects produced by factories
|
|
9
9
|
# (or doing other, interesting things with them (truly, only the code review is the limit!)).
|
|
10
10
|
# They are supposed to be immutable, shareable, and persistent:
|
|
11
11
|
# initialize once, use for the whole runtime.
|
|
12
12
|
#
|
|
13
13
|
# A simple mold can easily be just a +Proc+.
|
|
14
|
-
# All molds must have the following +#call+ signature: +call(
|
|
14
|
+
# All molds must have the following +#call+ signature: +call(forge_target:, attributes:, **)+.
|
|
15
15
|
# The extra keywords are ignored for possibility of future extensions.
|
|
16
16
|
#
|
|
17
17
|
# @example A very basic FactoryBot replacement
|
|
18
|
-
# creator = ->(
|
|
19
|
-
# instance =
|
|
18
|
+
# creator = ->(forge_target:, attributes:, **) do
|
|
19
|
+
# instance = forge_target.new
|
|
20
20
|
# attributes.each_pair { instance.public_send(:"#{_1}=", _2) }
|
|
21
21
|
# instance.save!
|
|
22
22
|
# end
|
|
23
|
-
#
|
|
23
|
+
#
|
|
24
|
+
# creator.call(forge_target: User, attributes: { name: "John", age: 30 })
|
|
24
25
|
# # => <User name="John" age=30>
|
|
26
|
+
#
|
|
25
27
|
# @example Using a mold to serialize collection of objects (contrivedly)
|
|
26
|
-
# dumpy = ->(
|
|
28
|
+
# dumpy = ->(forge_target:, attributes:, **) do
|
|
27
29
|
# Enumerator.new(attributes.size) do |y|
|
|
28
|
-
# attributes.each_pair { y <<
|
|
30
|
+
# attributes.each_pair { y << forge_target.dump(_1 => _2) }
|
|
29
31
|
# end
|
|
30
32
|
# end
|
|
31
|
-
#
|
|
33
|
+
#
|
|
34
|
+
# dumpy.call(forge_target: JSON, attributes: {a:1, b:2}).to_a
|
|
32
35
|
# # => ["{\"a\":1}", "{\"b\":2}"]
|
|
33
|
-
# dumpy.call(
|
|
36
|
+
# dumpy.call(forge_target: YAML, attributes: {a:1, b:2}).to_a
|
|
34
37
|
# # => ["---\n:a: 1\n", "---\n:b: 2\n"]
|
|
38
|
+
#
|
|
35
39
|
# @example Abstract factory pattern (kind of)
|
|
36
40
|
# class FurnitureFactory
|
|
37
|
-
# def call(
|
|
38
|
-
# concrete_factory = concrete_factory(
|
|
41
|
+
# def call(forge_target:, attributes:, **)
|
|
42
|
+
# concrete_factory = concrete_factory(forge_target)
|
|
39
43
|
# attributes[:pieces].map do |piece|
|
|
40
44
|
# concrete_factory.public_send(piece, attributes.dig(:color, piece))
|
|
41
45
|
# end
|
|
42
46
|
# end
|
|
47
|
+
#
|
|
43
48
|
# private def concrete_factory(style)
|
|
44
49
|
# case style
|
|
45
50
|
# when :hitech
|
|
@@ -49,20 +54,70 @@ module ObjectForge
|
|
|
49
54
|
# end
|
|
50
55
|
# end
|
|
51
56
|
# end
|
|
52
|
-
#
|
|
57
|
+
#
|
|
58
|
+
# FurnitureFactory.new.call(forge_target: :hitech, attributes: {
|
|
53
59
|
# pieces: [:chair, :table], color: { chair: :black, table: :white }
|
|
54
60
|
# })
|
|
55
61
|
# # => [<#HiTech::Chair color=:black>, <#HiTech::Table color=:white>]
|
|
62
|
+
#
|
|
56
63
|
# @example Abusing molds
|
|
57
|
-
# printer = ->(
|
|
58
|
-
# printer.call(
|
|
64
|
+
# printer = ->(forge_target:, attributes:, **) { PP.pp(attributes, forge_target) }
|
|
65
|
+
# printer.call(forge_target: $stderr, attributes: {a:1, b:2})
|
|
59
66
|
# # outputs "{:a=>1, :b=>2}" to $stderr
|
|
60
67
|
#
|
|
68
|
+
# @example Abuse above is just not enough, we need something even better
|
|
69
|
+
# class Character
|
|
70
|
+
# attr_reader :hp
|
|
71
|
+
#
|
|
72
|
+
# def initialize(hp)
|
|
73
|
+
# @hp = hp
|
|
74
|
+
# @damage_factory = ObjectForge::Forge.new(self, DamageParameters.new)
|
|
75
|
+
# end
|
|
76
|
+
#
|
|
77
|
+
# def hit(damage)
|
|
78
|
+
# if damage <= 0
|
|
79
|
+
# @damage_factory.call(:shielded, apply: ->(damage) { @hp -= damage })
|
|
80
|
+
# else
|
|
81
|
+
# @damage_factory.call(amount: damage, apply: ->(damage) { @hp -= damage })
|
|
82
|
+
# end
|
|
83
|
+
# end
|
|
84
|
+
#
|
|
85
|
+
# def heal(amount)
|
|
86
|
+
# @damage_factory.call(amount: amount, apply: ->(amount) { @hp += amount }) if amount >= 0
|
|
87
|
+
# end
|
|
88
|
+
#
|
|
89
|
+
# def die!
|
|
90
|
+
# puts "Character died!"
|
|
91
|
+
# end
|
|
92
|
+
# end
|
|
93
|
+
#
|
|
94
|
+
# class DamageParameters
|
|
95
|
+
# def attributes = {}
|
|
96
|
+
# def traits = { shielded: { amount: 1 } }
|
|
97
|
+
# def options
|
|
98
|
+
# {
|
|
99
|
+
# crucible: lambda(&:itself),
|
|
100
|
+
# mold: ->(forge_target:, attributes:, **) {
|
|
101
|
+
# attributes[:apply].call(attributes[:amount])
|
|
102
|
+
# forge_target
|
|
103
|
+
# },
|
|
104
|
+
# after_build: ->(forge_target) { forge_target.die! if forge_target.hp <= 0 }
|
|
105
|
+
# }
|
|
106
|
+
# end
|
|
107
|
+
# end
|
|
108
|
+
#
|
|
109
|
+
# mc = Character.new(100)
|
|
110
|
+
# mc.hit(50)
|
|
111
|
+
# mc.heal(25)
|
|
112
|
+
# mc.hit(-10)
|
|
113
|
+
# mc.hit(75)
|
|
114
|
+
# # outputs "Character died!"
|
|
115
|
+
#
|
|
61
116
|
# @since 0.2.0
|
|
62
117
|
module Molds
|
|
63
118
|
Dir["#{__dir__}/molds/*.rb"].each { require_relative _1 }
|
|
64
119
|
|
|
65
|
-
# Get maybe appropriate mold for the given
|
|
120
|
+
# Get maybe appropriate mold for the given forge target.
|
|
66
121
|
#
|
|
67
122
|
# Currently provides specific recognition for:
|
|
68
123
|
# - subclasses of +Struct+ ({StructMold}),
|
|
@@ -70,18 +125,18 @@ module ObjectForge
|
|
|
70
125
|
# - +Hash+ and subclasses ({HashMold}).
|
|
71
126
|
# Other objects just get {SingleArgumentMold}.
|
|
72
127
|
#
|
|
73
|
-
# @param
|
|
128
|
+
# @param forge_target [Class, Any]
|
|
74
129
|
# @return [#call] an instance of a mold
|
|
75
130
|
#
|
|
76
131
|
# @thread_safety Thread-safe.
|
|
77
132
|
# @since 0.3.0
|
|
78
|
-
def self.mold_for(
|
|
79
|
-
if ::Class ===
|
|
80
|
-
if
|
|
133
|
+
def self.mold_for(forge_target)
|
|
134
|
+
if ::Class === forge_target
|
|
135
|
+
if forge_target < ::Struct
|
|
81
136
|
StructMold.new
|
|
82
|
-
elsif defined?(::Data) &&
|
|
137
|
+
elsif defined?(::Data) && forge_target < ::Data
|
|
83
138
|
KeywordsMold.new
|
|
84
|
-
elsif
|
|
139
|
+
elsif forge_target <= ::Hash
|
|
85
140
|
HashMold.new
|
|
86
141
|
else
|
|
87
142
|
SingleArgumentMold.new
|
|
@@ -97,12 +152,10 @@ module ObjectForge
|
|
|
97
152
|
# If it is a Class with +#call+, wraps it in {WrappedMold}.
|
|
98
153
|
# Otherwise, raises an error.
|
|
99
154
|
#
|
|
100
|
-
# @since 0.3.0
|
|
101
|
-
#
|
|
102
155
|
# @param mold [Class, #call, nil]
|
|
103
156
|
# @return [#call, nil]
|
|
104
157
|
#
|
|
105
|
-
# @raise [
|
|
158
|
+
# @raise [ObjectInterfaceError] if +mold+ does not respond to or implement +#call+
|
|
106
159
|
#
|
|
107
160
|
# @thread_safety Thread-safe.
|
|
108
161
|
# @since 0.3.0
|
|
@@ -112,7 +165,7 @@ module ObjectForge
|
|
|
112
165
|
elsif ::Class === mold && mold.public_method_defined?(:call)
|
|
113
166
|
WrappedMold.new(mold)
|
|
114
167
|
else
|
|
115
|
-
raise
|
|
168
|
+
raise ObjectInterfaceError, "mold must respond to or implement #call"
|
|
116
169
|
end
|
|
117
170
|
end
|
|
118
171
|
end
|
|
@@ -25,10 +25,10 @@ module ObjectForge
|
|
|
25
25
|
#
|
|
26
26
|
# @param initial [#succ] initial value for the sequence
|
|
27
27
|
#
|
|
28
|
-
# @raise [
|
|
28
|
+
# @raise [ObjectInterfaceError] if +initial+ does not respond to #succ
|
|
29
29
|
def initialize(initial)
|
|
30
30
|
unless initial.respond_to?(:succ)
|
|
31
|
-
raise
|
|
31
|
+
raise ObjectInterfaceError, "initial value must respond to #succ"
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
@initial = initial
|
data/lib/object_forge/version.rb
CHANGED
data/lib/object_forge.rb
CHANGED
|
@@ -4,53 +4,113 @@ require_relative "object_forge/forgeyard"
|
|
|
4
4
|
require_relative "object_forge/sequence"
|
|
5
5
|
require_relative "object_forge/version"
|
|
6
6
|
|
|
7
|
-
# A
|
|
7
|
+
# A small factory library for Ruby objects with minimal assumptions about framework, persistence,
|
|
8
|
+
# or runtime environment.
|
|
8
9
|
#
|
|
9
10
|
# These are the main classes you should be aware of:
|
|
11
|
+
# - {Forge} is a factory for objects.
|
|
12
|
+
# Usually created through {Forgeyard#define} or {Forge.define}
|
|
13
|
+
# using a DSL similar to FactoryBot, Forges can be used standalone,
|
|
14
|
+
# or as a part of a Forgeyard.
|
|
10
15
|
# - {Forgeyard} is a registry of named related Forges.
|
|
11
16
|
# A Forgeyard allows to {Forgeyard#define} a Forge,
|
|
12
17
|
# and {Forgeyard#forge} a new object using a defined Forge.
|
|
13
|
-
# - {
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# - {Sequence} is a representation of a sequence of values.
|
|
17
|
-
# They are usually used implicitly through {ForgeDSL#sequence},
|
|
18
|
-
# but can be created explicitly to be shared (or used outside of ObjectForge).
|
|
18
|
+
# - {Molds} are object constructors used by {Forge}s.
|
|
19
|
+
# Several common molds are shipped with ObjectForge, but you
|
|
20
|
+
# will probably find it useful to create your own.
|
|
19
21
|
#
|
|
20
|
-
#
|
|
22
|
+
# Successful use may also depend on understanding these:
|
|
21
23
|
# - {ForgeDSL} is a block-based DSL inspired by FactoryBot and ROM::Factory.
|
|
22
24
|
# It allows defining arbitrary attributes (possibly using sequences),
|
|
23
25
|
# with support for traits (collections of attributes with non-default values).
|
|
26
|
+
# - {Sequence} is a representation of a sequence of values.
|
|
27
|
+
# They are usually used implicitly through {ForgeDSL#sequence},
|
|
28
|
+
# but can be created explicitly to be shared (or used outside of ObjectForge).
|
|
24
29
|
# - {Crucible} is used to resolve attributes.
|
|
25
|
-
#
|
|
30
|
+
#
|
|
31
|
+
# *ObjectForge* itself provides a top-level convenience API for working with a singular
|
|
32
|
+
# {DEFAULT_YARD} when you expect to never need more than one Forgeyard, such as in test suites.
|
|
26
33
|
#
|
|
27
34
|
# @example Quick example
|
|
28
35
|
# Frobinator = Struct.new(:frob, :inator, keyword_init: true)
|
|
29
|
-
#
|
|
36
|
+
#
|
|
37
|
+
# # Forge's name and target class are completely independent.
|
|
30
38
|
# ObjectForge.define(:frobber, Frobinator) do |f|
|
|
31
39
|
# f.frob { "Frob" + inator.call }
|
|
32
40
|
# f.inator { -> { "inator" } }
|
|
41
|
+
#
|
|
33
42
|
# f.trait :static do |tf|
|
|
34
43
|
# tf.frob { "Static" }
|
|
35
44
|
# end
|
|
36
45
|
# end
|
|
46
|
+
#
|
|
37
47
|
# # These methods are aliases:
|
|
38
48
|
# ObjectForge.forge(:frobber)
|
|
39
|
-
#
|
|
49
|
+
# # => #<struct Frobinator frob="Frobinator", inator=#<Proc:...>>
|
|
40
50
|
# ObjectForge.build(:frobber, frob: -> { "Frob" + inator }, inator: "orn")
|
|
41
|
-
#
|
|
51
|
+
# # => #<struct Frobinator frob="Froborn", inator="orn">
|
|
42
52
|
# ObjectForge.call(:frobber, :static, inator: "Value")
|
|
43
|
-
#
|
|
53
|
+
# # => #<struct Frobinator frob="Static", inator="Value">
|
|
54
|
+
#
|
|
55
|
+
# @example A more involved example
|
|
56
|
+
# require "logger"
|
|
57
|
+
#
|
|
58
|
+
# # A custom mold is needed for Logger because it has positional and keyword parameters.
|
|
59
|
+
# logger_mold = ->(forge_target:, attributes:, **) {
|
|
60
|
+
# forge_target.new(
|
|
61
|
+
# attributes[:file],
|
|
62
|
+
# *attributes[:rotation],
|
|
63
|
+
# **attributes.slice(:level, :progname, :formatter)
|
|
64
|
+
# )
|
|
65
|
+
# }
|
|
66
|
+
#
|
|
67
|
+
# # This is a one-off, universal factory, so using a Forgeyard is not needed.
|
|
68
|
+
# $logger_factory = ObjectForge::Forge.define(Logger) do |f|
|
|
69
|
+
# f.mold = logger_mold
|
|
70
|
+
# # Default crucible is almost always good enough, but let's use a simpler and faster resolver.
|
|
71
|
+
# f.crucible = ->(attributes) { attributes.transform_values { _1.is_a?(Proc) ? _1.call : _1 } }
|
|
72
|
+
#
|
|
73
|
+
# f.file { $stderr }
|
|
74
|
+
#
|
|
75
|
+
# f.trait :stdout do |tf|
|
|
76
|
+
# tf.file { $stdout }
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# f.trait :logfiles do |tf|
|
|
80
|
+
# require "date"
|
|
81
|
+
# tf.file { "log-#{Date.today}.log" }
|
|
82
|
+
# tf.rotation { "daily" }
|
|
83
|
+
# end
|
|
84
|
+
# end
|
|
85
|
+
#
|
|
86
|
+
# class MyClass
|
|
87
|
+
# def initialize
|
|
88
|
+
# @logger = $logger_factory.build(:stdout, progname: self.class)
|
|
89
|
+
# end
|
|
90
|
+
#
|
|
91
|
+
# def call
|
|
92
|
+
# @logger.info("called!")
|
|
93
|
+
# end
|
|
94
|
+
# end
|
|
95
|
+
#
|
|
96
|
+
# MyClass.new.call
|
|
97
|
+
# # outputs "I, [2026-05-25T13:56:33.533669 #206330] INFO -- MyClass: called!" to $stdout
|
|
44
98
|
module ObjectForge
|
|
45
|
-
# Base error class for ObjectForge.
|
|
99
|
+
# Base domain error class for ObjectForge.
|
|
46
100
|
# @since 0.1.0
|
|
47
101
|
class Error < StandardError; end
|
|
48
102
|
# Error raised when a mistake is made in using DSL.
|
|
49
103
|
# @since 0.1.0
|
|
50
104
|
class DSLError < Error; end
|
|
51
|
-
# Error raised when
|
|
52
|
-
# @since 0.
|
|
53
|
-
class
|
|
105
|
+
# Error raised when attribute resolution surfaces a circular dependency.
|
|
106
|
+
# @since 0.4.0
|
|
107
|
+
class CircularAttributeDependencyError < Error; end
|
|
108
|
+
|
|
109
|
+
# Error raised when object does not conform to expected interface,
|
|
110
|
+
# most commonly lacking +#call+.
|
|
111
|
+
# @note This class inherits from +TypeError+, not {Error}.
|
|
112
|
+
# @since 0.4.0
|
|
113
|
+
class ObjectInterfaceError < ::TypeError; end
|
|
54
114
|
|
|
55
115
|
# Default {Forgeyard} that is used by {.define} and {.forge}.
|
|
56
116
|
#
|
|
@@ -58,51 +118,53 @@ module ObjectForge
|
|
|
58
118
|
# @note
|
|
59
119
|
# Default forgeyard is intended to be useful for non-shareable code,
|
|
60
120
|
# like simple application tests and specs.
|
|
61
|
-
# It should not be used in application code,
|
|
121
|
+
# It should not be used in application code, especially in gems.
|
|
62
122
|
# @since 0.1.0
|
|
63
123
|
DEFAULT_YARD = Forgeyard.new
|
|
64
124
|
|
|
65
|
-
# @overload sequence(initial)
|
|
66
125
|
# Create a sequence, to be used wherever it needs to be.
|
|
67
126
|
#
|
|
68
127
|
# @see Sequence.new
|
|
69
128
|
# @since 0.1.0
|
|
70
129
|
#
|
|
71
|
-
# @
|
|
72
|
-
#
|
|
130
|
+
# @overload sequence(initial)
|
|
131
|
+
# @param initial [#succ, Sequence]
|
|
132
|
+
# @return [Sequence]
|
|
73
133
|
def self.sequence(...)
|
|
74
134
|
Sequence.new(...)
|
|
75
135
|
end
|
|
76
136
|
|
|
77
|
-
# @overload define(name, forged, &)
|
|
78
137
|
# Define and create a forge in {DEFAULT_YARD}.
|
|
79
138
|
#
|
|
80
139
|
# @!macro default_forgeyard
|
|
81
140
|
# @see Forgeyard#define
|
|
82
141
|
# @since 0.1.0
|
|
83
142
|
#
|
|
84
|
-
# @
|
|
85
|
-
#
|
|
86
|
-
#
|
|
87
|
-
#
|
|
88
|
-
#
|
|
143
|
+
# @overload define(name, forge_target)
|
|
144
|
+
# @param name [Symbol] name to register forge under
|
|
145
|
+
# @param forge_target [Class, Any] class or object to forge
|
|
146
|
+
# @yieldparam dsl [ForgeDSL]
|
|
147
|
+
# @yieldreturn [void]
|
|
148
|
+
# @return [Forge] forge
|
|
89
149
|
def self.define(...)
|
|
90
150
|
DEFAULT_YARD.define(...)
|
|
91
151
|
end
|
|
92
152
|
|
|
93
|
-
# @overload forge(name, *traits, **overrides, &)
|
|
94
153
|
# Build an instance using a forge from {DEFAULT_YARD}.
|
|
95
154
|
#
|
|
96
155
|
# @!macro default_forgeyard
|
|
97
156
|
# @see Forgeyard#forge
|
|
98
157
|
# @since 0.1.0
|
|
99
158
|
#
|
|
100
|
-
# @
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
159
|
+
# @overload forge(name, *traits, **overrides)
|
|
160
|
+
# @param name [Symbol] name of the forge
|
|
161
|
+
# @param traits [Array<Symbol>] traits to apply
|
|
162
|
+
# @param overrides [Hash{Symbol => Any}] attribute overrides
|
|
163
|
+
# @yieldparam object [Any] forged instance
|
|
164
|
+
# @yieldreturn [void]
|
|
165
|
+
# @return [Any] forged instance
|
|
166
|
+
# @raise [ArgumentError] if a trait name is unknown
|
|
167
|
+
# @raise [KeyError] if forge with the specified name is not registered
|
|
106
168
|
def self.forge(...)
|
|
107
169
|
DEFAULT_YARD.forge(...)
|
|
108
170
|
end
|
data/sig/object_forge/molds.rbs
CHANGED
|
@@ -3,15 +3,7 @@ module ObjectForge
|
|
|
3
3
|
def self.wrap_mold
|
|
4
4
|
: ((ObjectForge::mold | Class | nil) mold) -> ObjectForge::mold?
|
|
5
5
|
def self.mold_for
|
|
6
|
-
: (ObjectForge::
|
|
7
|
-
|
|
8
|
-
class SingleArgumentMold
|
|
9
|
-
include ObjectForge::_Mold
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
class KeywordsMold
|
|
13
|
-
include ObjectForge::_Mold
|
|
14
|
-
end
|
|
6
|
+
: (ObjectForge::forge_target) -> ObjectForge::mold
|
|
15
7
|
|
|
16
8
|
class WrappedMold
|
|
17
9
|
interface _MoldClass[T]
|
|
@@ -25,43 +17,53 @@ module ObjectForge
|
|
|
25
17
|
def initialize: (_MoldClass[ObjectForge::mold] wrapped_mold) -> void
|
|
26
18
|
end
|
|
27
19
|
|
|
20
|
+
class SingleArgumentMold
|
|
21
|
+
include ObjectForge::_Mold
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class KeywordsMold
|
|
25
|
+
include ObjectForge::_Mold
|
|
26
|
+
end
|
|
27
|
+
|
|
28
28
|
class StructMold
|
|
29
29
|
interface _StructSubclass[T]
|
|
30
30
|
def new
|
|
31
31
|
: (*untyped) -> T
|
|
32
32
|
| (**untyped) -> T
|
|
33
|
-
| (Hash[Symbol,
|
|
33
|
+
| (Hash[Symbol, ObjectForge::attribute]) -> T
|
|
34
34
|
def members: -> Array[Symbol]
|
|
35
35
|
def keyword_init?: -> bool?
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
RUBY_FEATURE_AUTO_KEYWORDS: bool
|
|
39
|
+
|
|
39
40
|
attr_reader lax: bool
|
|
41
|
+
alias lax? lax
|
|
40
42
|
|
|
41
43
|
def initialize: (?lax: bool) -> void
|
|
42
44
|
|
|
43
45
|
def call
|
|
44
|
-
: [T < Struct] (
|
|
46
|
+
: [T < Struct] (forge_target: _StructSubclass[T], attributes: Hash[Symbol, ObjectForge::attribute], **untyped) -> T
|
|
45
47
|
|
|
46
48
|
private
|
|
47
49
|
|
|
48
50
|
def build_struct_with_unspecified_keyword_init
|
|
49
|
-
: [T < Struct] (_StructSubclass[T]
|
|
51
|
+
: [T < Struct] (_StructSubclass[T] forge_target, Hash[Symbol, ObjectForge::attribute] attributes) -> T
|
|
50
52
|
end
|
|
51
53
|
|
|
52
54
|
class HashMold
|
|
53
55
|
interface _HashSubclass[T]
|
|
54
|
-
def []: (Hash[
|
|
56
|
+
def []: (Hash[Symbol, ObjectForge::attribute]) -> T
|
|
55
57
|
end
|
|
56
58
|
|
|
57
|
-
attr_reader default:
|
|
59
|
+
attr_reader default: ObjectForge::attribute
|
|
58
60
|
attr_reader default_proc: Proc?
|
|
59
61
|
|
|
60
62
|
def initialize
|
|
61
|
-
: (?
|
|
63
|
+
: [T < Hash] (?ObjectForge::attribute default_value) ?{ (T hash, untyped key) -> ObjectForge::attribute} -> void
|
|
62
64
|
|
|
63
65
|
def call
|
|
64
|
-
: [T < Hash] (
|
|
66
|
+
: [T < Hash] (forge_target: _HashSubclass[T], attributes: Hash[Symbol, ObjectForge::attribute], **untyped) -> T
|
|
65
67
|
end
|
|
66
68
|
end
|
|
67
69
|
end
|