striuct 0.0.11.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.
- data/.gemtest +0 -0
- data/History.rdoc +110 -0
- data/Manifest.txt +15 -0
- data/README.ja.rdoc +244 -0
- data/README.rdoc +220 -0
- data/Rakefile +22 -0
- data/example.rb +214 -0
- data/lib/striuct/classutil.rb +26 -0
- data/lib/striuct/core.rb +56 -0
- data/lib/striuct/import.rb +95 -0
- data/lib/striuct/subclass.rb +262 -0
- data/lib/striuct/subclass_eigen.rb +428 -0
- data/lib/striuct/version.rb +4 -0
- data/lib/striuct.rb +4 -0
- data/test/test_helper.rb +3 -0
- data/test/test_helper_import.rb +4 -0
- data/test/test_striuct.rb +580 -0
- data/test/test_striuct_import.rb +42 -0
- metadata +94 -0
@@ -0,0 +1,428 @@
|
|
1
|
+
class Striuct; module Subclass
|
2
|
+
|
3
|
+
# @author Kenichi Kamiya
|
4
|
+
module Eigen
|
5
|
+
|
6
|
+
INFERENCE = Object.new.freeze
|
7
|
+
|
8
|
+
NAMING_RISKS = {
|
9
|
+
conflict: 10,
|
10
|
+
no_identifier: 9,
|
11
|
+
bad_manners: 5,
|
12
|
+
no_ascii: 3,
|
13
|
+
strict: 0
|
14
|
+
}.freeze
|
15
|
+
|
16
|
+
PROTECT_LEVELS = {
|
17
|
+
struct: {error: 99, warn: 99},
|
18
|
+
warning: {error: 99, warn: 5},
|
19
|
+
error: {error: 9, warn: 5},
|
20
|
+
prevent: {error: 5, warn: 1},
|
21
|
+
nervous: {error: 1, warn: 1}
|
22
|
+
}.each(&:freeze).freeze
|
23
|
+
|
24
|
+
if respond_to? :private_constant
|
25
|
+
private_constant :INFERENCE, :NAMING_RISKS, :PROTECT_LEVELS
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
private
|
30
|
+
|
31
|
+
def extended(klass)
|
32
|
+
klass.class_eval do
|
33
|
+
@names, @conditions, @flavors, @defaults = [], {}, {}, {}
|
34
|
+
@inferences, @protect_level = {}, :prevent
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Subclass]
|
40
|
+
def new(*values)
|
41
|
+
new_instance(*values)
|
42
|
+
end
|
43
|
+
|
44
|
+
# @param [#each_pair] pairs ex: Hash
|
45
|
+
# @return [Subclass]
|
46
|
+
def load_pairs(pairs)
|
47
|
+
raise TypeError, 'no pairs object' unless pairs.respond_to? :each_pair
|
48
|
+
|
49
|
+
new.tap do |r|
|
50
|
+
pairs.each_pair do |name, value|
|
51
|
+
if member? name
|
52
|
+
r[name] = value
|
53
|
+
else
|
54
|
+
raise ArgumentError, " #{name} is not our member"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @yieldparam [Subclass] instance
|
61
|
+
# @yieldreturn [Subclass] instance
|
62
|
+
# @return [void]
|
63
|
+
def define(lock=true)
|
64
|
+
raise ArgumentError, 'must with block' unless block_given?
|
65
|
+
|
66
|
+
new.tap do |instance|
|
67
|
+
yield instance
|
68
|
+
|
69
|
+
if each_member.all?{|name|instance.assign? name}
|
70
|
+
instance.freeze if lock
|
71
|
+
else
|
72
|
+
raise "not yet finished"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Array<Symbol>]
|
78
|
+
def names
|
79
|
+
@names.dup
|
80
|
+
end
|
81
|
+
|
82
|
+
alias_method :members, :names
|
83
|
+
alias_method :keys, :names
|
84
|
+
|
85
|
+
def has_member?(name)
|
86
|
+
@names.include? keyable_for(name)
|
87
|
+
end
|
88
|
+
|
89
|
+
alias_method :member?, :has_member?
|
90
|
+
alias_method :has_key?, :has_member?
|
91
|
+
alias_method :key?, :has_key?
|
92
|
+
|
93
|
+
def has_conditions?(name)
|
94
|
+
raise NameError unless member? name
|
95
|
+
|
96
|
+
! @conditions[name].nil?
|
97
|
+
end
|
98
|
+
|
99
|
+
alias_method :restrict?, :has_conditions?
|
100
|
+
|
101
|
+
# @param [Symbol, String] name
|
102
|
+
# @param [Object] value
|
103
|
+
# @param [Object] context - expected own instance
|
104
|
+
def sufficent?(name, value, context=self)
|
105
|
+
name = keyable_for name
|
106
|
+
raise NameError unless member? name
|
107
|
+
|
108
|
+
if restrict? name
|
109
|
+
conditions_for(name).any?{|condition|
|
110
|
+
case condition
|
111
|
+
when Proc
|
112
|
+
context.instance_exec value, &condition
|
113
|
+
when Method
|
114
|
+
condition.call value
|
115
|
+
else
|
116
|
+
condition === value
|
117
|
+
end
|
118
|
+
}
|
119
|
+
else
|
120
|
+
true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
alias_method :accept?, :sufficent?
|
125
|
+
|
126
|
+
# @param [Symbol, String] name
|
127
|
+
def has_flavor?(name)
|
128
|
+
name = keyable_for name
|
129
|
+
raise NameError unless member? name
|
130
|
+
|
131
|
+
! @flavors[name].nil?
|
132
|
+
end
|
133
|
+
|
134
|
+
# @param [Symbol, String] name
|
135
|
+
def has_default?(name)
|
136
|
+
name = keyable_for name
|
137
|
+
raise NameError unless member? name
|
138
|
+
|
139
|
+
@defaults.has_key? name
|
140
|
+
end
|
141
|
+
|
142
|
+
# @yield [name]
|
143
|
+
# @yieldparam [Symbol] name - sequential under defined
|
144
|
+
# @yieldreturn [self]
|
145
|
+
# @return [Enumerator]
|
146
|
+
def each_name(&block)
|
147
|
+
return to_enum(__method__) unless block_given?
|
148
|
+
@names.each(&block)
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
alias_method :each_member, :each_name
|
153
|
+
alias_method :each_key, :each_name
|
154
|
+
|
155
|
+
# @return [Integer]
|
156
|
+
def length
|
157
|
+
@names.length
|
158
|
+
end
|
159
|
+
|
160
|
+
alias_method :size, :length
|
161
|
+
|
162
|
+
# @param [Object] name
|
163
|
+
def cname?(name)
|
164
|
+
check_safety_naming(keyable_for name){|r|r}
|
165
|
+
rescue Exception
|
166
|
+
false
|
167
|
+
end
|
168
|
+
|
169
|
+
# @param [Object] condition
|
170
|
+
def conditionable?(condition)
|
171
|
+
case condition
|
172
|
+
when Proc, Method
|
173
|
+
condition.arity == 1
|
174
|
+
else
|
175
|
+
condition.respond_to? :===
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def closed?
|
180
|
+
__stores__.any?(&:frozen?)
|
181
|
+
end
|
182
|
+
|
183
|
+
# @param [Symbol, String] name
|
184
|
+
def inference?(name)
|
185
|
+
name = keyable_for name
|
186
|
+
raise NameError unless member? name
|
187
|
+
|
188
|
+
@inferences.has_key? name
|
189
|
+
end
|
190
|
+
|
191
|
+
# @return [self]
|
192
|
+
def freeze
|
193
|
+
__stores__.each(&:freeze)
|
194
|
+
super
|
195
|
+
end
|
196
|
+
|
197
|
+
private
|
198
|
+
|
199
|
+
def inference
|
200
|
+
INFERENCE
|
201
|
+
end
|
202
|
+
|
203
|
+
def __stores__
|
204
|
+
[@names, @flavors, @defaults]
|
205
|
+
end
|
206
|
+
|
207
|
+
def initialize_copy(org)
|
208
|
+
@names, @flavors, @defaults = *__stores__.map(&:dup)
|
209
|
+
@conditions, @inferences, @protect_level = \
|
210
|
+
@conditions.dup, @inferences.dup, @protect_level
|
211
|
+
end
|
212
|
+
|
213
|
+
# @return [self]
|
214
|
+
def close
|
215
|
+
__stores__.each(&:freeze)
|
216
|
+
self
|
217
|
+
end
|
218
|
+
|
219
|
+
# @param [Symbol, String] name
|
220
|
+
# @return [Symbol]
|
221
|
+
def keyable_for(name)
|
222
|
+
case name
|
223
|
+
when Symbol, String
|
224
|
+
r = name.to_sym
|
225
|
+
|
226
|
+
if r.instance_of? Symbol
|
227
|
+
r
|
228
|
+
else
|
229
|
+
raise 'must not happen'
|
230
|
+
end
|
231
|
+
else
|
232
|
+
raise TypeError
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# @param [Symbol] name
|
237
|
+
# @return [Symbol]
|
238
|
+
def estimate_naming(name)
|
239
|
+
if (instance_methods + private_instance_methods).include? name
|
240
|
+
return :conflict
|
241
|
+
end
|
242
|
+
|
243
|
+
return :no_ascii unless name.encoding.equal? Encoding::ASCII
|
244
|
+
|
245
|
+
case name
|
246
|
+
when /[\W]/, /\A[^a-zA-Z_]/, :''
|
247
|
+
:no_identifier
|
248
|
+
when /\Aeach/, /\A__\w*__\z/, /[!?]\z/
|
249
|
+
:bad_manners
|
250
|
+
when /\A[a-zA-Z_]\w*\z/
|
251
|
+
:strict
|
252
|
+
else
|
253
|
+
raise 'must not happen'
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
# @param [Symbol] name
|
258
|
+
def check_safety_naming(name)
|
259
|
+
estimation = estimate_naming name
|
260
|
+
risk = NAMING_RISKS[estimation]
|
261
|
+
plevels = PROTECT_LEVELS[@protect_level]
|
262
|
+
caution = "undesirable naming '#{name}', because #{estimation}"
|
263
|
+
|
264
|
+
r = case
|
265
|
+
when risk >= plevels[:error]
|
266
|
+
raise NameError, caution unless block_given?
|
267
|
+
false
|
268
|
+
when risk >= plevels[:warn]
|
269
|
+
warn caution unless block_given?
|
270
|
+
false
|
271
|
+
else
|
272
|
+
true
|
273
|
+
end
|
274
|
+
|
275
|
+
yield r if block_given?
|
276
|
+
end
|
277
|
+
|
278
|
+
# @macro [attach] protect_level
|
279
|
+
# @param [Symbol] level
|
280
|
+
def protect_level(level)
|
281
|
+
raise NameError unless PROTECT_LEVELS.has_key? level
|
282
|
+
|
283
|
+
@protect_level = level
|
284
|
+
nil
|
285
|
+
end
|
286
|
+
|
287
|
+
# @macro [attach] member
|
288
|
+
# @return [nil]
|
289
|
+
def define_member(name, *conditions, &flavor)
|
290
|
+
raise "already closed to add member in #{self}" if closed?
|
291
|
+
name = keyable_for name
|
292
|
+
raise ArgumentError, %Q!already exist name "#{name}"! if member? name
|
293
|
+
check_safety_naming name
|
294
|
+
|
295
|
+
@names << name
|
296
|
+
__getter__! name
|
297
|
+
__setter__! name, *conditions, &flavor
|
298
|
+
nil
|
299
|
+
end
|
300
|
+
|
301
|
+
alias_method :def_member, :define_member
|
302
|
+
alias_method :member, :define_member
|
303
|
+
|
304
|
+
# @macro [attach] define_members
|
305
|
+
# @return [nil]
|
306
|
+
def define_members(*names)
|
307
|
+
raise "already closed to add members in #{self}" if closed?
|
308
|
+
unless names.length >= 1
|
309
|
+
raise ArgumentError, 'wrong number of arguments (0 for 1+)'
|
310
|
+
end
|
311
|
+
|
312
|
+
names.each do |name|
|
313
|
+
define_member name
|
314
|
+
end
|
315
|
+
|
316
|
+
nil
|
317
|
+
end
|
318
|
+
|
319
|
+
alias_method :def_members, :define_members
|
320
|
+
|
321
|
+
def __getter__!(name)
|
322
|
+
define_method name do
|
323
|
+
__get__ name
|
324
|
+
end
|
325
|
+
|
326
|
+
nil
|
327
|
+
end
|
328
|
+
|
329
|
+
def __setter__!(name, *conditions, &flavor)
|
330
|
+
__set_conditions__! name, *conditions
|
331
|
+
__set_flavor__! name, &flavor if block_given?
|
332
|
+
|
333
|
+
define_method "#{name}=" do |value|
|
334
|
+
__set__ name, value
|
335
|
+
end
|
336
|
+
|
337
|
+
nil
|
338
|
+
end
|
339
|
+
|
340
|
+
def __set_conditions__!(name, *conditions)
|
341
|
+
if conditions.reject!{|c|INFERENCE.equal? c}
|
342
|
+
@inferences[name] = true
|
343
|
+
end
|
344
|
+
|
345
|
+
unless conditions.empty?
|
346
|
+
if conditions.all?{|c|conditionable? c}
|
347
|
+
@conditions[name] = conditions
|
348
|
+
else
|
349
|
+
raise TypeError, 'wrong object for condition'
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
nil
|
354
|
+
end
|
355
|
+
|
356
|
+
def __set_flavor__!(name, &flavor)
|
357
|
+
if (arity = flavor.arity) == 1
|
358
|
+
@flavors[name] = flavor
|
359
|
+
else
|
360
|
+
raise ArgumentError, "wrong number of block argument #{arity} for 1"
|
361
|
+
end
|
362
|
+
|
363
|
+
nil
|
364
|
+
end
|
365
|
+
|
366
|
+
def __found_family__!(name, our)
|
367
|
+
family = our.class
|
368
|
+
|
369
|
+
unless name.instance_of?(Symbol) and inference?(name) and member?(name)
|
370
|
+
raise 'must not happen'
|
371
|
+
end
|
372
|
+
|
373
|
+
raise ArgumentError unless conditionable? family
|
374
|
+
|
375
|
+
@conditions[name] = [family]
|
376
|
+
@inferences.delete name
|
377
|
+
|
378
|
+
nil
|
379
|
+
end
|
380
|
+
|
381
|
+
def get_conditions(name)
|
382
|
+
name = keyable_for name
|
383
|
+
raise NameError, 'no defined member' unless member? name
|
384
|
+
|
385
|
+
@conditions[name]
|
386
|
+
end
|
387
|
+
|
388
|
+
alias_method :conditions_for, :get_conditions
|
389
|
+
|
390
|
+
def get_flavor(name)
|
391
|
+
name = keyable_for name
|
392
|
+
raise NameError, 'no defined member' unless member? name
|
393
|
+
|
394
|
+
@flavors[name]
|
395
|
+
end
|
396
|
+
|
397
|
+
alias_method :flavor_for, :get_flavor
|
398
|
+
|
399
|
+
# @param [Symbol, String] name
|
400
|
+
def get_default_value(name)
|
401
|
+
name = keyable_for name
|
402
|
+
raise NameError, 'no defined member' unless member? name
|
403
|
+
|
404
|
+
@defaults[name]
|
405
|
+
end
|
406
|
+
|
407
|
+
alias_method :default_for, :get_default_value
|
408
|
+
public :default_for
|
409
|
+
|
410
|
+
# @macro [attach] default
|
411
|
+
# @return [nil]
|
412
|
+
def set_default_value(name, value)
|
413
|
+
raise "already closed to modify member attributes in #{self}" if closed?
|
414
|
+
name = keyable_for name
|
415
|
+
raise NameError, 'no defined member' unless member? name
|
416
|
+
raise ConditionError unless accept? name, value
|
417
|
+
|
418
|
+
@defaults[name] = value
|
419
|
+
nil
|
420
|
+
end
|
421
|
+
|
422
|
+
alias_method :default, :set_default_value
|
423
|
+
|
424
|
+
|
425
|
+
end
|
426
|
+
|
427
|
+
|
428
|
+
end; end
|
data/lib/striuct.rb
ADDED
data/test/test_helper.rb
ADDED