object_forge 0.1.1 → 0.2.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: 05dce89141257f37f8b8af6a7f0cdfb1d7ee785a37ad119d0c13ff28c58d9245
4
- data.tar.gz: e171f2f3d504d0edadfdb5f2ba2f87d9d9edd1a0f175a0be366b12dc528182a0
3
+ metadata.gz: 83440b5319d1e909379d16b67830ad1f67f575358473dd4ba5914cb549c417b9
4
+ data.tar.gz: 279a5756a157f96252081cb60ce0541bdffdf1e21a25fe72db19b622c7d717e8
5
5
  SHA512:
6
- metadata.gz: c8a330629ce325a8bf78930a3025d5c1d75305e80c970cb5217e9d4ae6f93659d3fc9d3fdd89884920b5843c8c1bfb787bdfc6be8ce220db29124a2d73a749da
7
- data.tar.gz: a0515b9f4111c0e3bcd1b9bbb4f5cb3011bb12bba62750a2ca0f49b702f55a459be7877fb540cf16c6a40503a5c2294e962352408345e67bbb7179bc79f2539b
6
+ metadata.gz: e16b3091daf563ffffbb5da39b50a6cc2298a288a1e71528ab9de905e0c959718555ff8d3b6b2a3478f094fa3ebff8727f0a95e97c6adef05d3c7abb1ae6676f
7
+ data.tar.gz: 04455c660052171b8a4456323b700f91805d0c05190329427baee1468b42ccfee3c315b79534f997b45a1f2cd716f85076e164f8007cad821f43047a5ea8fcbf
@@ -12,7 +12,6 @@ module ObjectForge
12
12
  #
13
13
  # @thread_safety Attribute resolution is idempotent,
14
14
  # but modifies instance variables, making it unsafe to share the Crucible
15
- #
16
15
  # @since 0.1.0
17
16
  class Crucible < UnBasicObject
18
17
  %i[rand].each { |m| private define_method(m, ::Object.instance_method(m)) }
@@ -2,6 +2,7 @@
2
2
 
3
3
  require_relative "crucible"
4
4
  require_relative "forge_dsl"
5
+ require_relative "molds"
5
6
 
6
7
  module ObjectForge
7
8
  # Object instantitation forge.
@@ -19,14 +20,22 @@ module ObjectForge
19
20
  # @!attribute [r] traits
20
21
  # Attributes belonging to traits.
21
22
  # @return [Hash{Symbol => Hash{Symbol => Any}}]
22
- Parameters = Struct.new(:attributes, :traits, keyword_init: true)
23
+ #
24
+ # @!attribute [r] mold
25
+ # An object that knows how to build the instance.
26
+ # Must have a +call+ method that takes a class and a hash of attributes.
27
+ # @since 0.2.0
28
+ # @return [#call, nil]
29
+ Parameters = Struct.new(:attributes, :traits, :mold, keyword_init: true)
30
+
31
+ MOLD_MOLD = Molds::MoldMold.new.freeze
23
32
 
24
33
  # Define (and create) a forge using DSL.
25
34
  #
26
35
  # @see ForgeDSL
27
36
  # @thread_safety Thread-safe if DSL definition is thread-safe.
28
37
  #
29
- # @param forged [Class] class to forge
38
+ # @param forged [Class, Any] class or object to forge
30
39
  # @param name [Symbol, nil] forge name
31
40
  # @yieldparam f [ForgeDSL]
32
41
  # @yieldreturn [void]
@@ -38,19 +47,20 @@ module ObjectForge
38
47
  # @return [Symbol, nil] forge name
39
48
  attr_reader :name
40
49
 
41
- # @return [Class] class to forge
50
+ # @return [Class, Any] class or object to forge
42
51
  attr_reader :forged
43
52
 
44
53
  # @return [Parameters, ForgeDSL] forge parameters
45
54
  attr_reader :parameters
46
55
 
47
- # @param forged [Class] class to forge
56
+ # @param forged [Class, Any] class or object to forge
48
57
  # @param parameters [Parameters, ForgeDSL] forge parameters
49
58
  # @param name [Symbol, nil] forge name
50
59
  def initialize(forged, parameters, name: nil)
51
60
  @name = name
52
61
  @forged = forged
53
62
  @parameters = parameters
63
+ @mold = parameters.mold || MOLD_MOLD.call(forged: forged)
54
64
  end
55
65
 
56
66
  # Forge a new instance.
@@ -67,7 +77,7 @@ module ObjectForge
67
77
  # If a block is given, forged instance is yielded to it after being built.
68
78
  #
69
79
  # @thread_safety Forging is thread-safe if {#parameters},
70
- # +traits+ and +overrides+ are thread-safe.
80
+ # +traits+ and +overrides+ are thread-safe.
71
81
  #
72
82
  # @param traits [Array<Symbol>] traits to apply
73
83
  # @param overrides [Hash{Symbol => Any}] attribute overrides
@@ -76,7 +86,7 @@ module ObjectForge
76
86
  # @return [Any] built instance
77
87
  def forge(*traits, **overrides)
78
88
  resolved_attributes = resolve_attributes(traits, overrides)
79
- instance = build_instance(resolved_attributes)
89
+ instance = @mold.call(forged: @forged, attributes: resolved_attributes)
80
90
  yield instance if block_given?
81
91
  instance
82
92
  end
@@ -90,9 +100,5 @@ module ObjectForge
90
100
  attributes = @parameters.attributes.merge(*@parameters.traits.values_at(*traits), overrides)
91
101
  Crucible.new(attributes).resolve!
92
102
  end
93
-
94
- def build_instance(attributes)
95
- forged.new(attributes)
96
- end
97
103
  end
98
104
  end
@@ -3,6 +3,8 @@
3
3
  require_relative "sequence"
4
4
  require_relative "un_basic_object"
5
5
 
6
+ require_relative "molds/wrapped_mold"
7
+
6
8
  module ObjectForge
7
9
  # DSL for defining a forge.
8
10
  #
@@ -14,7 +16,6 @@ module ObjectForge
14
16
  # especially in attribute definitions.
15
17
  # The instance itself is frozen after initialization,
16
18
  # so it should be safe to share.
17
- #
18
19
  # @since 0.1.0
19
20
  class ForgeDSL < UnBasicObject
20
21
  # @return [Hash{Symbol => Proc}] attribute definitions
@@ -26,6 +27,9 @@ module ObjectForge
26
27
  # @return [Hash{Symbol => Hash{Symbol => Proc}}] trait definitions
27
28
  attr_reader :traits
28
29
 
30
+ # @return [#call, nil] forge mold
31
+ attr_reader :mold
32
+
29
33
  # Define forge's parameters through DSL.
30
34
  #
31
35
  # If the block has a parameter, an object will be yielded,
@@ -71,9 +75,34 @@ module ObjectForge
71
75
  @attributes.freeze
72
76
  @sequences.freeze
73
77
  @traits.freeze
78
+ @mold.freeze
74
79
  self
75
80
  end
76
81
 
82
+ # Set the forge mold.
83
+ #
84
+ # Mold is an object that knows how to take a hash of attributes
85
+ # and create an object from them.
86
+ # It can also be a class with +#call+, in which case a new mold will be instantiated
87
+ # automatically for each build. If a single instance is enough,
88
+ # please call +.new+ yourself once.
89
+ #
90
+ # @since 0.2.0
91
+ #
92
+ # @param mold [Class, #call, nil]
93
+ # @return [Class, #call, nil]
94
+ #
95
+ # @raise [DSLError] if +mold+ does not respond to or implement +#call+
96
+ def mold=(mold)
97
+ if nil == mold || mold.respond_to?(:call) # rubocop:disable Style/YodaCondition
98
+ @mold = mold
99
+ elsif ::Class === mold && mold.public_method_defined?(:call)
100
+ @mold = Molds::WrappedMold.new(mold)
101
+ else
102
+ raise DSLError, "mold must respond to or implement #call"
103
+ end
104
+ end
105
+
77
106
  # Define an attribute, possibly transient.
78
107
  #
79
108
  # DSL does not know or care what attributes the forged class has,
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "concurrent/map"
4
4
 
5
+ require_relative "forge"
6
+
5
7
  module ObjectForge
6
8
  # A registry for forges, making them accessible by name.
7
9
  #
@@ -20,7 +22,7 @@ module ObjectForge
20
22
  # @see Forge.define
21
23
  #
22
24
  # @param name [Symbol] name to register forge under
23
- # @param forged [Class] class to forge
25
+ # @param forged [Class, Any] class or object to forge
24
26
  # @yieldparam f [ForgeDSL]
25
27
  # @yieldreturn [void]
26
28
  # @return [Forge] forge
@@ -55,8 +57,8 @@ module ObjectForge
55
57
  # @return [Any] built instance
56
58
  #
57
59
  # @raise [KeyError] if forge with the specified name is not registered
58
- def forge(name, *traits, **overrides, &)
59
- @forges.fetch(name)[*traits, **overrides, &]
60
+ def forge(name, ...)
61
+ @forges.fetch(name).[](...)
60
62
  end
61
63
 
62
64
  alias build forge
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ObjectForge
4
+ module Molds
5
+ # Mold for constructing Hashes.
6
+ #
7
+ # @thread_safety Thread-safe on its own,
8
+ # but using unshareable default value or block is not thread-safe.
9
+ #
10
+ # @since 0.2.0
11
+ class HashMold
12
+ # Default value to be assigned to each produced hash.
13
+ # @return [Any, nil]
14
+ attr_reader :default
15
+ # Default proc to be assigned to each produced hash.
16
+ # @return [Proc, nil]
17
+ attr_reader :default_proc
18
+
19
+ # Initialize new HashMold with default value or default proc
20
+ # to be assigned to each produced hash.
21
+ #
22
+ # The same exact objects are used for each hash.
23
+ # It is not advised to use mutable objects as default values.
24
+ # Be aware that using a default proc with assignment
25
+ # is inherently not safe, see this Ruby issue:
26
+ # https://bugs.ruby-lang.org/issues/19237.
27
+ #
28
+ # @see Hash.new
29
+ #
30
+ # @param default_value [Any]
31
+ # @yieldparam hash [Hash]
32
+ # @yieldparam key [Any]
33
+ # @yieldreturn [Any]
34
+ def initialize(default_value = nil, &default_proc)
35
+ @default = default_value
36
+ @default_proc = default_proc
37
+ end
38
+
39
+ # Build a new hash using +forged.[]+.
40
+ #
41
+ # @see Hash.[]
42
+ #
43
+ # @param forged [Class] Hash or a subclass of Hash
44
+ # @param attributes [Hash{Symbol => Any}]
45
+ # @return [Hash]
46
+ def call(forged:, attributes:, **_)
47
+ hash = forged[attributes]
48
+ hash.default = @default if @default
49
+ hash.default_proc = @default_proc if @default_proc
50
+ hash
51
+ end
52
+ end
53
+ end
54
+ end
@@ -1,19 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # module ObjectForge
4
- # module Molds
5
- # # Mold which calls +forged.new(**attributes)+.
6
- # #
7
- # # @since 0.1.1
8
- # class KeywordsMold
9
- # # Instantiate +forged+ with a hash of attributes.
10
- # #
11
- # # @param forged [Class]
12
- # # @param attributes [Hash{Symbol => Any}]
13
- # # @return [Any]
14
- # def call(forged:, attributes:, **)
15
- # forged.new(**attributes)
16
- # end
17
- # end
18
- # end
19
- # end
3
+ module ObjectForge
4
+ module Molds
5
+ # Basic mold which calls +forged.new(**attributes)+.
6
+ #
7
+ # Can be used instead of {SingleArgumentMold},
8
+ # but performance is about 1.5 times worse.
9
+ #
10
+ # @thread_safety Thread-safe.
11
+ # @since 0.2.0
12
+ class KeywordsMold
13
+ # Instantiate +forged+ with a hash of attributes.
14
+ #
15
+ # @param forged [Class, #new]
16
+ # @param attributes [Hash{Symbol => Any}]
17
+ # @return [Any]
18
+ def call(forged:, attributes:, **_)
19
+ forged.new(**attributes)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ObjectForge
4
+ module Molds
5
+ # Special "mold" that returns appropriate mold for the given forged object.
6
+ # Probably not the best fit though.
7
+ #
8
+ # Currently provides specific recognition for:
9
+ # - subclasses of +Struct+ ({StructMold}),
10
+ # - subclasses of +Data+ ({KeywordsMold}),
11
+ # - +Hash+ and subclasses ({HashMold}).
12
+ # Other objects just get {SingleArgumentMold}.
13
+ #
14
+ # @thread_safety Thread-safe.
15
+ # @since 0.2.0
16
+ class MoldMold
17
+ # Get maybe appropriate mold for the given forged object.
18
+ #
19
+ # @param forged [Class, Any]
20
+ # @return [#call] an instance of a mold
21
+ def call(forged:, **_)
22
+ # rubocop:disable Style/YodaCondition
23
+ if ::Class === forged
24
+ if ::Struct > forged
25
+ StructMold.new
26
+ elsif defined?(::Data) && ::Data > forged
27
+ KeywordsMold.new
28
+ elsif ::Hash >= forged
29
+ HashMold.new
30
+ else
31
+ SingleArgumentMold.new
32
+ end
33
+ else
34
+ SingleArgumentMold.new
35
+ end
36
+ # rubocop:enable Style/YodaCondition
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ObjectForge
4
+ module Molds
5
+ # Basic mold which calls +forged.new(attributes)+.
6
+ #
7
+ # @thread_safety Thread-safe.
8
+ # @since 0.2.0
9
+ class SingleArgumentMold
10
+ # Instantiate +forged+ with a hash of attributes.
11
+ #
12
+ # @param forged [Class, #new]
13
+ # @param attributes [Hash{Symbol => Any}]
14
+ # @return [Any]
15
+ def call(forged:, attributes:, **_)
16
+ forged.new(attributes)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ObjectForge
4
+ module Molds
5
+ # Mold for building Structs.
6
+ #
7
+ # Supports all variations of +keyword_init+.
8
+ #
9
+ # @thread_safety Thread-safe.
10
+ # @since 0.2.0
11
+ class StructMold
12
+ # Does Struct automatically use keyword initialization
13
+ # when +keyword_init+ is not specified / +nil+?
14
+ #
15
+ # @return [Boolean]
16
+ RUBY_FEATURE_AUTO_KEYWORDS = (::Struct.new(:a, :b).new(a: 1, b: 2).a == 1)
17
+
18
+ # Whether to work around argument hashes with extra keys.
19
+ #
20
+ # @return [Boolean]
21
+ attr_reader :lax
22
+ alias lax? lax
23
+
24
+ # @param lax [Boolean]
25
+ # whether to work around argument hashes with extra keys
26
+ # (when keyword_init is false, workaround always happens for technical reasons)
27
+ # - if +true+, arguments can contain extra keys, but building is slower;
28
+ # - if +false+, building may raise an error if extra keys are present;
29
+ def initialize(lax: true)
30
+ @lax = lax
31
+ end
32
+
33
+ # Instantiate +forged+ struct with a hash of attributes.
34
+ #
35
+ # @param forged [Class] a subclass of Struct
36
+ # @param attributes [Hash{Symbol => Any}]
37
+ # @return [Struct]
38
+ def call(forged:, attributes:, **_)
39
+ if forged.keyword_init?
40
+ lax ? forged.new(attributes.slice(*forged.members)) : forged.new(attributes)
41
+ elsif forged.keyword_init? == false
42
+ forged.new(*attributes.values_at(*forged.members))
43
+ else
44
+ build_struct_with_unspecified_keyword_init(forged, attributes)
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ if RUBY_FEATURE_AUTO_KEYWORDS
51
+ # Build struct by using keywords to specify member values.
52
+ def build_struct_with_unspecified_keyword_init(forged, attributes)
53
+ if lax
54
+ forged.new(**attributes.slice(*forged.members))
55
+ else
56
+ forged.new(**attributes)
57
+ end
58
+ end
59
+ else
60
+ # :nocov:
61
+ # Build struct by using positional arguments to specify member values.
62
+ def build_struct_with_unspecified_keyword_init(forged, attributes)
63
+ forged.new(*attributes.values_at(*forged.members))
64
+ end
65
+ # :nocov:
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ObjectForge
4
+ module Molds
5
+ # Mold that wraps a mold class.
6
+ #
7
+ # Wrapping a mold class is useful when its +#call+ is stateful,
8
+ # making it unsafe to use multiple times or in shared environments.
9
+ #
10
+ # @thread_safety Thread-safe if {wrapped_mold} does not use global state.
11
+ # @since 0.2.0
12
+ class WrappedMold
13
+ # @return [Class] wrapped mold class
14
+ attr_reader :wrapped_mold
15
+
16
+ # @param wrapped_mold [Class] class with +#call+ method
17
+ def initialize(wrapped_mold)
18
+ @wrapped_mold = wrapped_mold
19
+ end
20
+
21
+ # @overload call(...)
22
+ # Instantiate {wrapped_mold} and call it.
23
+ #
24
+ # @return [Any] result of +wrapped_mold.new.call(...)+
25
+ def call(...)
26
+ wrapped_mold.new.call(...)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ObjectForge
4
+ # This module provides a collection of predefined molds to be used in common cases.
5
+ #
6
+ # Molds are +#call+able objects responsible for actually building objects produced by factories
7
+ # (or doing other, interesting things with them (truly, only the code review is the limit!)).
8
+ # They are supposed to be immutable, shareable, and persistent:
9
+ # initialize once, use for the whole runtime.
10
+ #
11
+ # A simple mold can easily be just a +Proc+.
12
+ # All molds must have the following +#call+ signature: +call(forged:, attributes:, **)+.
13
+ # The extra keywords are for future extensions.
14
+ #
15
+ # @example A very basic FactoryBot replacement
16
+ # creator = ->(forged:, attributes:, **) do
17
+ # instance = forged.new
18
+ # attributes.each_pair { instance.public_send(:"#{_1}=", _2) }
19
+ # instance.save!
20
+ # end
21
+ # creator.call(forged: User, attributes: { name: "John", age: 30 })
22
+ # # => <User name="John" age=30>
23
+ # @example Using a mold to serialize collection of objects (contrivedly)
24
+ # dumpy = ->(forged:, attributes:, **) do
25
+ # Enumerator.new(attributes.size) do |y|
26
+ # attributes.each_pair { y << forged.dump(_1 => _2) }
27
+ # end
28
+ # end
29
+ # dumpy.call(forged: JSON, attributes: {a:1, b:2}).to_a
30
+ # # => ["{\"a\":1}", "{\"b\":2}"]
31
+ # dumpy.call(forged: YAML, attributes: {a:1, b:2}).to_a
32
+ # # => ["---\n:a: 1\n", "---\n:b: 2\n"]
33
+ # @example Abstract factory pattern (kind of)
34
+ # class FurnitureFactory
35
+ # def call(forged:, attributes:, **)
36
+ # concrete_factory = concrete_factory(forged)
37
+ # attributes[:pieces].map do |piece|
38
+ # concrete_factory.public_send(piece, attributes.dig(:color, piece))
39
+ # end
40
+ # end
41
+ # private def concrete_factory(style)
42
+ # case style
43
+ # when :hitech
44
+ # HiTechFactory.new
45
+ # when :retro
46
+ # RetroFactory.new
47
+ # end
48
+ # end
49
+ # end
50
+ # FurnitureFactory.new.call(forged: :hitech, attributes: {
51
+ # pieces: [:chair, :table], color: { chair: :black, table: :white }
52
+ # })
53
+ # # => [<#HiTech::Chair color=:black>, <#HiTech::Table color=:white>]
54
+ # @example Abusing molds
55
+ # printer = ->(forged:, attributes:, **) { PP.pp(attributes, forged) }
56
+ # printer.call(forged: $stderr, attributes: {a:1, b:2})
57
+ # # outputs "{:a=>1, :b=>2}" to $stderr
58
+ #
59
+ # @since 0.2.0
60
+ module Molds
61
+ Dir["#{__dir__}/molds/*.rb"].each { require_relative _1 }
62
+ end
63
+ end
@@ -4,7 +4,6 @@ module ObjectForge
4
4
  # BasicObject with a few common methods copied from Object.
5
5
  #
6
6
  # @api private
7
- #
8
7
  # @since 0.1.0
9
8
  class UnBasicObject < ::BasicObject
10
9
  # @!group Instance methods copied from Object
@@ -2,5 +2,5 @@
2
2
 
3
3
  module ObjectForge
4
4
  # Current version
5
- VERSION = "0.1.1"
5
+ VERSION = "0.2.0"
6
6
  end
data/lib/object_forge.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- Dir["#{__dir__}/object_forge/**/*.rb"].each { require _1 }
3
+ require_relative "object_forge/forgeyard"
4
+ require_relative "object_forge/sequence"
5
+ require_relative "object_forge/version"
4
6
 
5
7
  # A simple all-purpose factory library with minimal assumptions.
6
8
  #
@@ -76,7 +78,7 @@ module ObjectForge
76
78
  # @since 0.1.0
77
79
  #
78
80
  # @param name [Symbol] forge name
79
- # @param forged [Class] class to forge
81
+ # @param forged [Class, Any] class or object to forge
80
82
  # @yieldparam f [ForgeDSL]
81
83
  # @yieldreturn [void]
82
84
  # @return [Forge] forge
@@ -0,0 +1,72 @@
1
+ module ObjectForge
2
+ interface _Mold
3
+ def call
4
+ : (forged: untyped, attributes: Hash[Symbol, untyped], **untyped) -> untyped
5
+ end
6
+
7
+ module Molds
8
+ class MoldMold
9
+ def call
10
+ : (forged: untyped, **untyped) -> ObjectForge::_Mold
11
+ end
12
+
13
+ class SingleArgumentMold
14
+ include ObjectForge::_Mold
15
+ end
16
+
17
+ class KeywordsMold
18
+ include ObjectForge::_Mold
19
+ end
20
+
21
+ class WrappedMold
22
+ interface _MoldClass[T]
23
+ def new: () -> T
24
+ end
25
+
26
+ include ObjectForge::_Mold
27
+
28
+ attr_reader wrapped_mold: _MoldClass[ObjectForge::mold]
29
+
30
+ def initialize: (_MoldClass[ObjectForge::mold] wrapped_mold) -> void
31
+ end
32
+
33
+ class StructMold
34
+ interface _StructSubclass[T]
35
+ def new
36
+ : (*untyped) -> T
37
+ | (**untyped) -> T
38
+ | (Hash[Symbol, untyped]) -> T
39
+ def members: -> Array[Symbol]
40
+ def keyword_init?: -> bool?
41
+ end
42
+
43
+ RUBY_FEATURE_AUTO_KEYWORDS: bool
44
+ attr_reader lax: bool
45
+
46
+ def initialize: (?lax: bool) -> void
47
+
48
+ def call
49
+ : [T < Struct] (forged: _StructSubclass[T], attributes: Hash[Symbol, untyped], **untyped) -> T
50
+
51
+ private
52
+
53
+ def build_struct_with_unspecified_keyword_init
54
+ : [T < Struct] (_StructSubclass[T] forged, Hash[Symbol, untyped] attributes) -> T
55
+ end
56
+
57
+ class HashMold
58
+ interface _HashSubclass[T]
59
+ def []: (Hash[untyped, untyped]) -> T
60
+ end
61
+
62
+ attr_reader default: untyped?
63
+ attr_reader default_proc: Proc?
64
+
65
+ def initialize
66
+ : (?untyped? default_value) ?{ (Hash[untyped, untyped] hash, untyped key) -> untyped} -> void
67
+
68
+ def call
69
+ : [T < Hash] (forged: _HashSubclass[T], attributes: Hash[Symbol, untyped], **untyped) -> T
70
+ end
71
+ end
72
+ end
data/sig/object_forge.rbs CHANGED
@@ -1,11 +1,12 @@
1
1
  module ObjectForge
2
+ type mold = ObjectForge::_RespondTo & ObjectForge::_Mold
2
3
  type sequenceable = ObjectForge::_RespondTo & ObjectForge::_Sequenceable
3
4
 
4
5
  interface _RespondTo
5
6
  def respond_to?: (Symbol name, ?bool include_private) -> bool
6
7
  def class: -> Class
7
8
  end
8
- interface _Sequenceable
9
+ interface _Sequenceable
9
10
  def succ: -> self
10
11
  end
11
12
  interface _Forgable
@@ -14,6 +15,7 @@ interface _Sequenceable
14
15
  interface _ForgeParameters
15
16
  def attributes: () -> Hash[Symbol, untyped]
16
17
  def traits: () -> Hash[Symbol, Hash[Symbol, untyped]]
18
+ def mold: () -> ObjectForge::mold?
17
19
  end
18
20
 
19
21
  class Error < StandardError
@@ -76,6 +78,8 @@ class ObjectForge::Forge
76
78
  : (attributes: Hash[Symbol, untyped], traits: Hash[Symbol, Hash[Symbol, untyped]]) -> void
77
79
  end
78
80
 
81
+ MOLD_MOLD: ObjectForge::Molds::MoldMold
82
+
79
83
  attr_reader forged: ObjectForge::_Forgable
80
84
  attr_reader name: Symbol
81
85
 
@@ -95,9 +99,6 @@ class ObjectForge::Forge
95
99
 
96
100
  def resolve_attributes
97
101
  : (Array[Symbol] traits, Hash[Symbol, untyped] overrides) -> Hash[Symbol, untyped]
98
-
99
- def build_instance
100
- : (Hash[Symbol, untyped] attributes) -> ObjectForge::_Forgable
101
102
  end
102
103
 
103
104
  class ObjectForge::ForgeDSL < ObjectForge::UnBasicObject
@@ -115,6 +116,9 @@ class ObjectForge::ForgeDSL < ObjectForge::UnBasicObject
115
116
 
116
117
  def freeze: -> self
117
118
 
119
+ def mold=
120
+ : (ObjectForge::mold) -> void
121
+
118
122
  def attribute
119
123
  : (Symbol name) { [self: ObjectForge::Crucible] -> untyped } -> Symbol
120
124
  alias [] attribute
metadata CHANGED
@@ -1,13 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: object_forge
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexandr Bulancov
8
+ autorequire:
8
9
  bindir: exe
9
10
  cert_chain: []
10
- date: 1980-01-02 00:00:00.000000000 Z
11
+ date: 2025-08-20 00:00:00.000000000 Z
11
12
  dependencies:
12
13
  - !ruby/object:Gem::Dependency
13
14
  name: concurrent-ruby
@@ -30,6 +31,7 @@ description: |
30
31
  To use, just define some factories and call them wherever you need,
31
32
  be it in tests, console, or application code.
32
33
  If needed, almost any part of the process can be easily replaced with a custom solution.
34
+ email:
33
35
  executables: []
34
36
  extensions: []
35
37
  extra_rdoc_files: []
@@ -39,21 +41,29 @@ files:
39
41
  - lib/object_forge/forge.rb
40
42
  - lib/object_forge/forge_dsl.rb
41
43
  - lib/object_forge/forgeyard.rb
44
+ - lib/object_forge/molds.rb
45
+ - lib/object_forge/molds/hash_mold.rb
42
46
  - lib/object_forge/molds/keywords_mold.rb
47
+ - lib/object_forge/molds/mold_mold.rb
48
+ - lib/object_forge/molds/single_argument_mold.rb
49
+ - lib/object_forge/molds/struct_mold.rb
50
+ - lib/object_forge/molds/wrapped_mold.rb
43
51
  - lib/object_forge/sequence.rb
44
52
  - lib/object_forge/un_basic_object.rb
45
53
  - lib/object_forge/version.rb
46
54
  - sig/object_forge.rbs
55
+ - sig/object_forge/molds.rbs
47
56
  homepage: https://github.com/trinistr/object_forge
48
57
  licenses:
49
58
  - MIT
50
59
  metadata:
51
60
  homepage_uri: https://github.com/trinistr/object_forge
52
61
  bug_tracker_uri: https://github.com/trinistr/object_forge/issues
53
- documentation_uri: https://rubydoc.info/gems/object_forge/0.1.1
54
- source_code_uri: https://github.com/trinistr/object_forge/tree/v0.1.1
55
- changelog_uri: https://github.com/trinistr/object_forge/blob/v0.1.1/CHANGELOG.md
62
+ documentation_uri: https://rubydoc.info/gems/object_forge/0.2.0
63
+ source_code_uri: https://github.com/trinistr/object_forge/tree/v0.2.0
64
+ changelog_uri: https://github.com/trinistr/object_forge/blob/v0.2.0/CHANGELOG.md
56
65
  rubygems_mfa_required: 'true'
66
+ post_install_message:
57
67
  rdoc_options:
58
68
  - "--tag"
59
69
  - thread_safety:Thread safety
@@ -70,7 +80,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
70
80
  - !ruby/object:Gem::Version
71
81
  version: '0'
72
82
  requirements: []
73
- rubygems_version: 3.7.1
83
+ rubygems_version: 3.5.22
84
+ signing_key:
74
85
  specification_version: 4
75
86
  summary: A simple factory for objects with minimal assumptions.
76
87
  test_files: []