@@ -56,17 +56,23 @@ module Define
56
56
# # Access it from metadata
57
57
# subaru_baja.metadata[:all_wheel_drive] # => true
58
58
#
59
+ # @example Making a copy with an extended definition
60
+ # # Create an instance with `.define`:
61
+ # subaru_baja = Car.define do
62
+ # make "Subaru"
63
+ # model "Baja"
64
+ # doors 4
65
+ # end
66
+ #
67
+ # # Then extend it with `#redefine`
68
+ # two_door_baja = subaru_baja.redefine do
69
+ # doors 2
70
+ # end
59
71
module InstanceDefinable
60
72
def self . included ( base )
61
73
base . extend ( ClassMethods )
62
74
end
63
75
64
- # Set the definition block for this instance.
65
- # It can be run later with {#ensure_defined}
66
- def definition_proc = ( defn_block )
67
- @definition_proc = defn_block
68
- end
69
-
70
76
# `metadata` can store arbitrary key-values with an object.
71
77
#
72
78
# @return [Hash<Object, Object>] Hash for user-defined storage
@@ -84,19 +90,21 @@ def metadata
84
90
def define ( **kwargs , &block )
85
91
# make sure the previous definition_proc was executed:
86
92
ensure_defined
87
-
88
- @definition_proc = -> ( obj ) {
89
- kwargs . each do |keyword , value |
90
- public_send ( keyword , value )
91
- end
92
-
93
- if block
94
- instance_eval ( &block )
95
- end
96
- }
93
+ @pending_definition = Definition . new ( kwargs , block )
97
94
nil
98
95
end
99
96
97
+ # Make a new instance of this class, then
98
+ # re-run any definitions on that object.
99
+ # @return [InstanceDefinable] A new instance, with any extended definitions
100
+ def redefine ( **kwargs , &block )
101
+ ensure_defined
102
+ new_instance = self . class . new
103
+ applied_definitions . each { |defn | defn . apply ( new_instance ) }
104
+ new_instance . define ( **kwargs , &block )
105
+ new_instance
106
+ end
107
+
100
108
private
101
109
102
110
# Run the definition block if it hasn't been run yet.
@@ -105,15 +113,39 @@ def define(**kwargs, &block)
105
113
# come from the definition block.
106
114
# @return [void]
107
115
def ensure_defined
108
- if @definition_proc
109
- defn_proc = @definition_proc
110
- @definition_proc = nil
111
- proxy = DefinedObjectProxy . new ( self )
112
- proxy . instance_eval ( & defn_proc )
116
+ if @pending_definition
117
+ defn = @pending_definition
118
+ @pending_definition = nil
119
+ defn . apply ( self )
120
+ applied_definitions << defn
113
121
end
114
122
nil
115
123
end
116
124
125
+ def applied_definitions
126
+ @applied_definitions ||= [ ]
127
+ end
128
+
129
+
130
+ class Definition
131
+ def initialize ( define_keywords , define_proc )
132
+ @define_keywords = define_keywords
133
+ @define_proc = define_proc
134
+ end
135
+
136
+ def apply ( instance )
137
+ defn_proxy = DefinedObjectProxy . new ( instance )
138
+
139
+ @define_keywords . each do |keyword , value |
140
+ defn_proxy . public_send ( keyword , value )
141
+ end
142
+
143
+ if @define_proc
144
+ defn_proxy . instance_eval ( &@define_proc )
145
+ end
146
+ end
147
+ end
148
+
117
149
module ClassMethods
118
150
# Create a new instance
119
151
# and prepare a definition using its {.definitions}.
0 commit comments