striuct 0.0.11.1 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,428 +0,0 @@
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
@@ -1,4 +0,0 @@
1
- require 'stringio'
2
- require 'test/unit'
3
- require File.dirname(__FILE__) + '/../lib/striuct'
4
- require File.dirname(__FILE__) + '/../lib/striuct/import'
@@ -1,42 +0,0 @@
1
- $VERBOSE = true
2
- require File.dirname(__FILE__) + '/test_helper_import.rb'
3
-
4
-
5
- class TestStruct < Test::Unit::TestCase
6
- Sth = Struct.new :age
7
- Sth2 = Sth.to_strict
8
-
9
- def test_eigen_boolean
10
- assert_equal true, Sth.member?(:age)
11
- assert_equal false, Sth.conditionable?(BasicObject.new)
12
- assert_equal false, Sth.restrict?(:age)
13
- assert_equal true, Sth.accept?(:age, 'String')
14
- assert_equal false, Sth.has_default?(:age)
15
- assert_equal true, Sth.cname?(:age)
16
- assert_equal false, Sth.cname?(Object.new)
17
- end
18
-
19
- def test_eigen_actions
20
- r = Sth.define{|o|o.age = 'Something'}
21
- assert_kind_of Sth, r
22
- assert_equal 'Something', r.age
23
- assert_equal StrictStruct, Sth2.superclass
24
- assert_equal Sth.members, Sth2.members
25
-
26
- r2 = Sth.load_pairs age: 1
27
- assert_equal 1, r2.age
28
-
29
- assert_raises ArgumentError do
30
- Sth.load_pairs age: 1, name: 'taro'
31
- end
32
- end
33
-
34
- def test_instance_boolean
35
- sth = Sth.new
36
- assert_equal false, sth.strict?
37
- assert_equal false, sth.secure?
38
- assert_equal false, sth.assign?(:age)
39
- sth.age = 'Something'
40
- assert_equal true, sth.assign?(:age)
41
- end
42
- end