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.
@@ -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
@@ -0,0 +1,4 @@
1
+ class Striuct
2
+ VERSION = '0.0.11.1'.freeze
3
+ Version = VERSION
4
+ end
data/lib/striuct.rb ADDED
@@ -0,0 +1,4 @@
1
+ require_relative 'striuct/version'
2
+ require_relative 'striuct/core'
3
+
4
+ StrictStruct = Striuct
@@ -0,0 +1,3 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/striuct'
@@ -0,0 +1,4 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/striuct'
4
+ require File.dirname(__FILE__) + '/../lib/striuct/import'