core-state 0.1.8 → 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 +4 -4
- data/CHANGELOG.md +26 -19
- data/lib/core/state/version.rb +1 -1
- data/lib/core/state.rb +226 -2
- metadata +23 -10
- data/lib/is/stateful.rb +0 -295
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 477f7141fabe96eb7a23f595f9577e672e371e985dd986120ed3310d302ed95a
|
4
|
+
data.tar.gz: 032c9fdaa012fb0f5fed35817d23aa693944005f1254deeb3a87e10e73c130e0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3e44fde7c140c8268b51ee49d182f4240ab231ec8129fa57e9c11d97a3f915aca5ba376074eeb56ae11942293bda67007efb379501d0ab28c810aea658efe6ad
|
7
|
+
data.tar.gz: 931bf990e70cfb5e28975229ed0d8168e6efc324c786fa7d572b75a07a82fd7ee21e747ca7c46e3e166cd8e6b402775470261e82a22a7323b72f1e3412cfec28
|
data/CHANGELOG.md
CHANGED
@@ -1,57 +1,64 @@
|
|
1
|
-
## [v0.
|
1
|
+
## [v0.2.0](https://github.com/bryanp/corerb/releases/tag/2023-12-24)
|
2
|
+
|
3
|
+
*released on 2023-12-24*
|
4
|
+
|
5
|
+
* `dep` [#143](https://github.com/bryanp/corerb/pull/143) Deprecate `Is::*` and `Refine::*` namespaces ([bryanp](https://github.com/bryanp))
|
6
|
+
* `dep` [#135](https://github.com/bryanp/corerb/pull/135) Remove Ruby 2 support ([bryanp](https://github.com/bryanp))
|
7
|
+
|
8
|
+
## [v0.1.8](https://github.com/bryanp/corerb/releases/tag/2023-05-07)
|
2
9
|
|
3
10
|
*released on 2023-05-07*
|
4
11
|
|
5
12
|
* `fix` [#132](https://github.com/bryanp/corerb/pull/132) Fix state inheritence ([bryanp](https://github.com/bryanp))
|
6
13
|
|
7
|
-
## [v0.1.7](https://github.com/
|
14
|
+
## [v0.1.7](https://github.com/bryanp/corerb/releases/tag/2021-11-23.1)
|
8
15
|
|
9
16
|
*released on 2021-11-23*
|
10
17
|
|
11
|
-
* `chg` [#109](https://github.com/
|
18
|
+
* `chg` [#109](https://github.com/bryanp/corerb/pull/109) Prefer `__send__` to `send` ([bryanp](https://github.com/bryanp))
|
12
19
|
|
13
|
-
## [v0.1.6](https://github.com/
|
20
|
+
## [v0.1.6](https://github.com/bryanp/corerb/releases/tag/2021-11-02)
|
14
21
|
|
15
22
|
*released on 2021-11-02*
|
16
23
|
|
17
|
-
* `chg` [#97](https://github.com/
|
24
|
+
* `chg` [#97](https://github.com/bryanp/corerb/pull/97) Designate internal state with leading and trailing double underscores ([bryanp](https://github.com/bryanp))
|
18
25
|
|
19
|
-
## [v0.1.5](https://github.com/
|
26
|
+
## [v0.1.5](https://github.com/bryanp/corerb/releases/tag/2021-10-22)
|
20
27
|
|
21
28
|
*released on 2021-10-22*
|
22
29
|
|
23
|
-
* `fix` [#88](https://github.com/
|
30
|
+
* `fix` [#88](https://github.com/bryanp/corerb/pull/88) Fix an issue causing state to be shared between sibling subclasses ([bryanp](https://github.com/bryanp))
|
24
31
|
|
25
|
-
## [v0.1.4](https://github.com/
|
32
|
+
## [v0.1.4](https://github.com/bryanp/corerb/releases/tag/2021-09-30)
|
26
33
|
|
27
34
|
*released on 2021-09-30*
|
28
35
|
|
29
|
-
* `fix` [#85](https://github.com/
|
36
|
+
* `fix` [#85](https://github.com/bryanp/corerb/pull/85) Initialize with class-level state when available ([bryanp](https://github.com/bryanp))
|
30
37
|
|
31
|
-
## [v0.1.3](https://github.com/
|
38
|
+
## [v0.1.3](https://github.com/bryanp/corerb/releases/tag/2021-09-28)
|
32
39
|
|
33
40
|
*released on 2021-09-28*
|
34
41
|
|
35
|
-
* `fix` [#84](https://github.com/
|
42
|
+
* `fix` [#84](https://github.com/bryanp/corerb/pull/84) Define core-local as a dependency of core-state ([bryanp](https://github.com/bryanp))
|
36
43
|
|
37
|
-
## [v0.1.1](https://github.com/
|
44
|
+
## [v0.1.1](https://github.com/bryanp/corerb/releases/tag/2021-07-15)
|
38
45
|
|
39
46
|
*released on 2021-07-15*
|
40
47
|
|
41
|
-
* `fix` [#62](https://github.com/
|
42
|
-
* `fix` [#55](https://github.com/
|
48
|
+
* `fix` [#62](https://github.com/bryanp/corerb/pull/62) Correctly isolate class-level state on access ([bryanp](https://github.com/bryanp))
|
49
|
+
* `fix` [#55](https://github.com/bryanp/corerb/pull/55) Correctly forward arguments to super initialize ([bryanp](https://github.com/bryanp))
|
43
50
|
|
44
|
-
## [v0.1.0](https://github.com/
|
51
|
+
## [v0.1.0](https://github.com/bryanp/corerb/releases/tag/2021-07-07)
|
45
52
|
|
46
53
|
*released on 2021-07-07*
|
47
54
|
|
48
|
-
* `fix` [#48](https://github.com/
|
49
|
-
* `chg` [#44](https://github.com/
|
55
|
+
* `fix` [#48](https://github.com/bryanp/corerb/pull/48) Correctly copy defined state when a stateful object is copied ([bryanp](https://github.com/bryanp))
|
56
|
+
* `chg` [#44](https://github.com/bryanp/corerb/pull/44) Drop Ruby 2.6 support from core-state ([bryanp](https://github.com/bryanp))
|
50
57
|
|
51
|
-
## [v0.0.0](https://github.com/
|
58
|
+
## [v0.0.0](https://github.com/bryanp/corerb/releases/tag/2021-05-22)
|
52
59
|
|
53
60
|
*released on 2021-05-22*
|
54
61
|
|
55
|
-
* `add` [#31](https://github.com/
|
62
|
+
* `add` [#31](https://github.com/bryanp/corerb/pull/31) Initial implementation of core-state ([bryanp](https://github.com/bryanp))
|
56
63
|
|
57
64
|
|
data/lib/core/state/version.rb
CHANGED
data/lib/core/state.rb
CHANGED
@@ -1,9 +1,233 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "core/copy"
|
4
|
+
require "core/extension"
|
5
|
+
require "core/local"
|
6
|
+
|
3
7
|
module Core
|
8
|
+
# [public] Make objects stateful.
|
9
|
+
#
|
4
10
|
module State
|
5
11
|
require_relative "state/version"
|
12
|
+
|
13
|
+
extend Core::Extension
|
14
|
+
|
15
|
+
using Core::Copy
|
16
|
+
|
17
|
+
ALLOWED_FLAGS = %i[class instance].freeze
|
18
|
+
|
19
|
+
applies do
|
20
|
+
prepend_defined_state_modules
|
21
|
+
end
|
22
|
+
|
23
|
+
extends :definition do
|
24
|
+
# [public] Defines state by name.
|
25
|
+
#
|
26
|
+
# flags - Changes how the state is defined. Possible values include:
|
27
|
+
#
|
28
|
+
# * `:class` - Defines state at the class level.
|
29
|
+
#
|
30
|
+
# * `:instance` - Defines state at the instance level.
|
31
|
+
#
|
32
|
+
# Dependencies are applied with both the `:class` and `:instance` flags by default.
|
33
|
+
#
|
34
|
+
# default - The default value for the defined state.
|
35
|
+
#
|
36
|
+
# private - If `true`, the reader/writer will be defined as private.
|
37
|
+
#
|
38
|
+
# reader - If `true`, defines a reader for the state.
|
39
|
+
#
|
40
|
+
# writer - If `true`, defines a reader for the state.
|
41
|
+
#
|
42
|
+
def state(name, *flags, default: nil, private: false, reader: true, writer: true)
|
43
|
+
flags = flags.map(&:to_sym)
|
44
|
+
enforce_allowed_flags(flags)
|
45
|
+
|
46
|
+
state = {
|
47
|
+
value: default,
|
48
|
+
class: define_class_state?(flags),
|
49
|
+
instance: define_instance_state?(flags)
|
50
|
+
}
|
51
|
+
|
52
|
+
method_name = name.to_sym
|
53
|
+
ivar_name = "@#{method_name}"
|
54
|
+
defined_state[ivar_name] = state
|
55
|
+
defined_state_isolations << ivar_name
|
56
|
+
|
57
|
+
if state[:class]
|
58
|
+
instance_variable_set(ivar_name, default)
|
59
|
+
end
|
60
|
+
|
61
|
+
prefix = if private
|
62
|
+
"private "
|
63
|
+
else
|
64
|
+
""
|
65
|
+
end
|
66
|
+
|
67
|
+
if reader
|
68
|
+
if state[:class]
|
69
|
+
defined_state_class_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
|
70
|
+
#{prefix}def #{method_name}
|
71
|
+
unless defined_state_isolations.include?(#{ivar_name.inspect})
|
72
|
+
#{ivar_name} = #{ivar_name}.copy
|
73
|
+
defined_state_isolations << #{ivar_name.inspect}
|
74
|
+
end
|
75
|
+
|
76
|
+
#{ivar_name}
|
77
|
+
end
|
78
|
+
CODE
|
79
|
+
end
|
80
|
+
|
81
|
+
if state[:instance]
|
82
|
+
defined_state_instance_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
|
83
|
+
#{prefix}def #{method_name}
|
84
|
+
unless defined_state_isolations.include?(#{ivar_name.inspect})
|
85
|
+
#{ivar_name} = #{ivar_name}.copy
|
86
|
+
defined_state_isolations << #{ivar_name.inspect}
|
87
|
+
end
|
88
|
+
|
89
|
+
#{ivar_name}
|
90
|
+
end
|
91
|
+
CODE
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
if writer
|
96
|
+
if state[:class]
|
97
|
+
defined_state_class_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
|
98
|
+
#{prefix}def #{method_name}=(value)
|
99
|
+
defined_state[#{ivar_name.inspect}][:value] = value
|
100
|
+
#{ivar_name} = value
|
101
|
+
end
|
102
|
+
CODE
|
103
|
+
end
|
104
|
+
|
105
|
+
if state[:instance]
|
106
|
+
defined_state_instance_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
|
107
|
+
#{prefix}def #{method_name}=(value)
|
108
|
+
#{ivar_name} = value
|
109
|
+
end
|
110
|
+
CODE
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if state[:class]
|
115
|
+
instance_variable_set(ivar_name, default)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def inherited(subclass)
|
120
|
+
defined_state.each_pair do |name, state|
|
121
|
+
if state[:class]
|
122
|
+
subclass.instance_variable_set(name, instance_variable_get(name))
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
subclass.__send__(:prepend_defined_state_modules)
|
127
|
+
subclass.instance_variable_set(:@__defined_state__, @__defined_state__.each_with_object({}) { |(key, value), state|
|
128
|
+
state[key] = value.dup
|
129
|
+
})
|
130
|
+
|
131
|
+
super
|
132
|
+
end
|
133
|
+
|
134
|
+
private def defined_state
|
135
|
+
@__defined_state__ ||= {}
|
136
|
+
end
|
137
|
+
|
138
|
+
private def defined_state_class_module
|
139
|
+
@__defined_state_class_module__ ||= Module.new
|
140
|
+
end
|
141
|
+
|
142
|
+
private def defined_state_instance_module
|
143
|
+
@__defined_state_instance_module__ ||= Module.new
|
144
|
+
end
|
145
|
+
|
146
|
+
private def prepend_defined_state_modules
|
147
|
+
prepend(defined_state_instance_module)
|
148
|
+
singleton_class.prepend(defined_state_class_module)
|
149
|
+
end
|
150
|
+
|
151
|
+
private def enforce_allowed_flags(flags)
|
152
|
+
flags.each do |flag|
|
153
|
+
unless allowed_flag?(flag)
|
154
|
+
raise ArgumentError, "Expected flag `#{flag.inspect}' to be one of: #{allowed_flags_string}"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
private def allowed_flag?(flag)
|
160
|
+
ALLOWED_FLAGS.include?(flag)
|
161
|
+
end
|
162
|
+
|
163
|
+
private def allowed_flags_string
|
164
|
+
ALLOWED_FLAGS.map { |allowed_flag|
|
165
|
+
"`#{allowed_flag.inspect}'"
|
166
|
+
}.join(", ")
|
167
|
+
end
|
168
|
+
|
169
|
+
private def define_class_state?(flags)
|
170
|
+
flags.empty? || flags.include?(:class)
|
171
|
+
end
|
172
|
+
|
173
|
+
private def define_instance_state?(flags)
|
174
|
+
flags.empty? || flags.include?(:instance)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
extends :implementation, prepend: true do
|
179
|
+
def initialize(...)
|
180
|
+
self_class = self.class
|
181
|
+
self_class.__send__(:defined_state).each_pair do |name, state|
|
182
|
+
if state[:instance]
|
183
|
+
if state[:class]
|
184
|
+
instance_variable_set(name, self_class.instance_variable_get(name))
|
185
|
+
else
|
186
|
+
instance_variable_set(name, state[:value])
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
super
|
192
|
+
end
|
193
|
+
|
194
|
+
def initialize_copy(...)
|
195
|
+
@__defined_state_isolations__ = []
|
196
|
+
|
197
|
+
super
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
extends :definition, :implementation do
|
202
|
+
# [public] Safely mutates state by name, yielding a copy of the current value to the block.
|
203
|
+
#
|
204
|
+
def mutate_state(name, &block)
|
205
|
+
__send__("#{name}=", block.call(__send__(name)))
|
206
|
+
end
|
207
|
+
|
208
|
+
private def defined_state_isolations
|
209
|
+
@__defined_state_isolations__ ||= []
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
class << self
|
214
|
+
include Core::Local
|
215
|
+
|
216
|
+
def prevent_recursion(object)
|
217
|
+
object_id = object.object_id
|
218
|
+
copied_objects[object_id] = true
|
219
|
+
yield
|
220
|
+
ensure
|
221
|
+
copied_objects.delete(object_id)
|
222
|
+
end
|
223
|
+
|
224
|
+
def copying?(object)
|
225
|
+
copied_objects[object.object_id]
|
226
|
+
end
|
227
|
+
|
228
|
+
def copied_objects
|
229
|
+
localized(:__corerb_copied_objects) || localize(:__corerb_copied_objects, {})
|
230
|
+
end
|
231
|
+
end
|
6
232
|
end
|
7
233
|
end
|
8
|
-
|
9
|
-
require_relative "../is/stateful"
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: core-state
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryan Powell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: core-
|
14
|
+
name: core-copy
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
@@ -24,22 +24,36 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: core-extension
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.5'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.5'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: core-local
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
30
44
|
requirements:
|
31
45
|
- - "~>"
|
32
46
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
47
|
+
version: '0.2'
|
34
48
|
type: :runtime
|
35
49
|
prerelease: false
|
36
50
|
version_requirements: !ruby/object:Gem::Requirement
|
37
51
|
requirements:
|
38
52
|
- - "~>"
|
39
53
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0.
|
54
|
+
version: '0.2'
|
41
55
|
description: Easily manage object state.
|
42
|
-
email: bryan@
|
56
|
+
email: bryan@bryanp.org
|
43
57
|
executables: []
|
44
58
|
extensions: []
|
45
59
|
extra_rdoc_files: []
|
@@ -48,8 +62,7 @@ files:
|
|
48
62
|
- LICENSE
|
49
63
|
- lib/core/state.rb
|
50
64
|
- lib/core/state/version.rb
|
51
|
-
|
52
|
-
homepage: https://github.com/metabahn/corerb/
|
65
|
+
homepage: https://github.com/bryanp/corerb/
|
53
66
|
licenses:
|
54
67
|
- MPL-2.0
|
55
68
|
metadata: {}
|
@@ -61,14 +74,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
61
74
|
requirements:
|
62
75
|
- - ">="
|
63
76
|
- !ruby/object:Gem::Version
|
64
|
-
version: '
|
77
|
+
version: '3.0'
|
65
78
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
79
|
requirements:
|
67
80
|
- - ">="
|
68
81
|
- !ruby/object:Gem::Version
|
69
82
|
version: '0'
|
70
83
|
requirements: []
|
71
|
-
rubygems_version: 3.
|
84
|
+
rubygems_version: 3.5.1
|
72
85
|
signing_key:
|
73
86
|
specification_version: 4
|
74
87
|
summary: Easily manage object state.
|
data/lib/is/stateful.rb
DELETED
@@ -1,295 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "core/extension"
|
4
|
-
require "core/local"
|
5
|
-
|
6
|
-
module Is
|
7
|
-
# [public] Makes objects stateful.
|
8
|
-
#
|
9
|
-
module Stateful
|
10
|
-
# Adding core-copy as a dependency creates a recursive dependency, so just bundle it.
|
11
|
-
#
|
12
|
-
module Copy
|
13
|
-
DEFAULT = ::Object.new
|
14
|
-
|
15
|
-
refine ::Object do
|
16
|
-
if RbConfig::CONFIG["RUBY_PROGRAM_VERSION"] < "3"
|
17
|
-
def copy(freeze: DEFAULT)
|
18
|
-
should_freeze = resolve_freeze_argument(freeze)
|
19
|
-
|
20
|
-
value = clone(freeze: should_freeze)
|
21
|
-
value.freeze if should_freeze
|
22
|
-
value
|
23
|
-
end
|
24
|
-
else
|
25
|
-
def copy(freeze: DEFAULT)
|
26
|
-
clone(freeze: resolve_freeze_argument(freeze))
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
private def resolve_freeze_argument(value)
|
31
|
-
case value
|
32
|
-
when DEFAULT
|
33
|
-
frozen?
|
34
|
-
else
|
35
|
-
!!value
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
refine Array do
|
41
|
-
def copy(freeze: DEFAULT)
|
42
|
-
unless Stateful.copying?(self)
|
43
|
-
Stateful.prevent_recursion(self) do
|
44
|
-
array = map { |value|
|
45
|
-
value.copy(freeze: freeze)
|
46
|
-
}
|
47
|
-
|
48
|
-
array.freeze if resolve_freeze_argument(freeze)
|
49
|
-
|
50
|
-
array
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
refine Hash do
|
57
|
-
def copy(freeze: DEFAULT)
|
58
|
-
unless Stateful.copying?(self)
|
59
|
-
Stateful.prevent_recursion(self) do
|
60
|
-
hash = {}
|
61
|
-
|
62
|
-
each_pair do |key, value|
|
63
|
-
hash[key.copy(freeze: freeze)] = value.copy(freeze: freeze)
|
64
|
-
end
|
65
|
-
|
66
|
-
hash.freeze if resolve_freeze_argument(freeze)
|
67
|
-
|
68
|
-
hash
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
extend Is::Extension
|
76
|
-
|
77
|
-
using Copy
|
78
|
-
|
79
|
-
ALLOWED_FLAGS = %i[class instance].freeze
|
80
|
-
|
81
|
-
applies do
|
82
|
-
prepend_defined_state_modules
|
83
|
-
end
|
84
|
-
|
85
|
-
extends :definition do
|
86
|
-
# [public] Defines state by name.
|
87
|
-
#
|
88
|
-
# flags - Changes how the state is defined. Possible values include:
|
89
|
-
#
|
90
|
-
# * `:class` - Defines state at the class level.
|
91
|
-
#
|
92
|
-
# * `:instance` - Defines state at the instance level.
|
93
|
-
#
|
94
|
-
# Dependencies are applied with both the `:class` and `:instance` flags by default.
|
95
|
-
#
|
96
|
-
# default - The default value for the defined state.
|
97
|
-
#
|
98
|
-
# private - If `true`, the reader/writer will be defined as private.
|
99
|
-
#
|
100
|
-
# reader - If `true`, defines a reader for the state.
|
101
|
-
#
|
102
|
-
# writer - If `true`, defines a reader for the state.
|
103
|
-
#
|
104
|
-
def state(name, *flags, default: nil, private: false, reader: true, writer: true)
|
105
|
-
flags = flags.map(&:to_sym)
|
106
|
-
enforce_allowed_flags(flags)
|
107
|
-
|
108
|
-
state = {
|
109
|
-
value: default,
|
110
|
-
class: define_class_state?(flags),
|
111
|
-
instance: define_instance_state?(flags)
|
112
|
-
}
|
113
|
-
|
114
|
-
method_name = name.to_sym
|
115
|
-
ivar_name = "@#{method_name}"
|
116
|
-
defined_state[ivar_name] = state
|
117
|
-
defined_state_isolations << ivar_name
|
118
|
-
|
119
|
-
if state[:class]
|
120
|
-
instance_variable_set(ivar_name, default)
|
121
|
-
end
|
122
|
-
|
123
|
-
prefix = if private
|
124
|
-
"private "
|
125
|
-
else
|
126
|
-
""
|
127
|
-
end
|
128
|
-
|
129
|
-
if reader
|
130
|
-
if state[:class]
|
131
|
-
defined_state_class_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
|
132
|
-
#{prefix}def #{method_name}
|
133
|
-
unless defined_state_isolations.include?(#{ivar_name.inspect})
|
134
|
-
#{ivar_name} = #{ivar_name}.copy
|
135
|
-
defined_state_isolations << #{ivar_name.inspect}
|
136
|
-
end
|
137
|
-
|
138
|
-
#{ivar_name}
|
139
|
-
end
|
140
|
-
CODE
|
141
|
-
end
|
142
|
-
|
143
|
-
if state[:instance]
|
144
|
-
defined_state_instance_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
|
145
|
-
#{prefix}def #{method_name}
|
146
|
-
unless defined_state_isolations.include?(#{ivar_name.inspect})
|
147
|
-
#{ivar_name} = #{ivar_name}.copy
|
148
|
-
defined_state_isolations << #{ivar_name.inspect}
|
149
|
-
end
|
150
|
-
|
151
|
-
#{ivar_name}
|
152
|
-
end
|
153
|
-
CODE
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
if writer
|
158
|
-
if state[:class]
|
159
|
-
defined_state_class_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
|
160
|
-
#{prefix}def #{method_name}=(value)
|
161
|
-
defined_state[#{ivar_name.inspect}][:value] = value
|
162
|
-
#{ivar_name} = value
|
163
|
-
end
|
164
|
-
CODE
|
165
|
-
end
|
166
|
-
|
167
|
-
if state[:instance]
|
168
|
-
defined_state_instance_module.module_eval <<~CODE, __FILE__, __LINE__ + 1
|
169
|
-
#{prefix}def #{method_name}=(value)
|
170
|
-
#{ivar_name} = value
|
171
|
-
end
|
172
|
-
CODE
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
if state[:class]
|
177
|
-
instance_variable_set(ivar_name, default)
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def inherited(subclass)
|
182
|
-
defined_state.each_pair do |name, state|
|
183
|
-
if state[:class]
|
184
|
-
subclass.instance_variable_set(name, instance_variable_get(name))
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
|
-
subclass.__send__(:prepend_defined_state_modules)
|
189
|
-
subclass.instance_variable_set(:@__defined_state__, @__defined_state__.each_with_object({}) { |(key, value), state|
|
190
|
-
state[key] = value.dup
|
191
|
-
})
|
192
|
-
|
193
|
-
super
|
194
|
-
end
|
195
|
-
|
196
|
-
private def defined_state
|
197
|
-
@__defined_state__ ||= {}
|
198
|
-
end
|
199
|
-
|
200
|
-
private def defined_state_class_module
|
201
|
-
@__defined_state_class_module__ ||= Module.new
|
202
|
-
end
|
203
|
-
|
204
|
-
private def defined_state_instance_module
|
205
|
-
@__defined_state_instance_module__ ||= Module.new
|
206
|
-
end
|
207
|
-
|
208
|
-
private def prepend_defined_state_modules
|
209
|
-
prepend(defined_state_instance_module)
|
210
|
-
singleton_class.prepend(defined_state_class_module)
|
211
|
-
end
|
212
|
-
|
213
|
-
private def enforce_allowed_flags(flags)
|
214
|
-
flags.each do |flag|
|
215
|
-
unless allowed_flag?(flag)
|
216
|
-
raise ArgumentError, "Expected flag `#{flag.inspect}' to be one of: #{allowed_flags_string}"
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
private def allowed_flag?(flag)
|
222
|
-
ALLOWED_FLAGS.include?(flag)
|
223
|
-
end
|
224
|
-
|
225
|
-
private def allowed_flags_string
|
226
|
-
ALLOWED_FLAGS.map { |allowed_flag|
|
227
|
-
"`#{allowed_flag.inspect}'"
|
228
|
-
}.join(", ")
|
229
|
-
end
|
230
|
-
|
231
|
-
private def define_class_state?(flags)
|
232
|
-
flags.empty? || flags.include?(:class)
|
233
|
-
end
|
234
|
-
|
235
|
-
private def define_instance_state?(flags)
|
236
|
-
flags.empty? || flags.include?(:instance)
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
extends :implementation, prepend: true do
|
241
|
-
def initialize(...)
|
242
|
-
self_class = self.class
|
243
|
-
self_class.__send__(:defined_state).each_pair do |name, state|
|
244
|
-
if state[:instance]
|
245
|
-
if state[:class]
|
246
|
-
instance_variable_set(name, self_class.instance_variable_get(name))
|
247
|
-
else
|
248
|
-
instance_variable_set(name, state[:value])
|
249
|
-
end
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
super
|
254
|
-
end
|
255
|
-
|
256
|
-
def initialize_copy(...)
|
257
|
-
@__defined_state_isolations__ = []
|
258
|
-
|
259
|
-
super
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
|
-
extends :definition, :implementation do
|
264
|
-
# [public] Safely mutates state by name, yielding a copy of the current value to the block.
|
265
|
-
#
|
266
|
-
def mutate_state(name, &block)
|
267
|
-
__send__("#{name}=", block.call(__send__(name)))
|
268
|
-
end
|
269
|
-
|
270
|
-
private def defined_state_isolations
|
271
|
-
@__defined_state_isolations__ ||= []
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
class << self
|
276
|
-
include Is::Localized
|
277
|
-
|
278
|
-
def prevent_recursion(object)
|
279
|
-
object_id = object.object_id
|
280
|
-
copied_objects[object_id] = true
|
281
|
-
yield
|
282
|
-
ensure
|
283
|
-
copied_objects.delete(object_id)
|
284
|
-
end
|
285
|
-
|
286
|
-
def copying?(object)
|
287
|
-
copied_objects[object.object_id]
|
288
|
-
end
|
289
|
-
|
290
|
-
def copied_objects
|
291
|
-
localized(:__corerb_copied_objects) || localize(:__corerb_copied_objects, {})
|
292
|
-
end
|
293
|
-
end
|
294
|
-
end
|
295
|
-
end
|