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/example.rb ADDED
@@ -0,0 +1,214 @@
1
+ #/usr/bin/ruby -w
2
+
3
+ require_relative 'lib/striuct'
4
+
5
+ def debug(message)
6
+ puts "line: #{caller[0].slice(/:(\w+)/, 1)}"
7
+ puts message.inspect, '-' * 80
8
+ end
9
+
10
+ #* Macro "member" provides one of Struct+ interfaces for condtions and a flavor.
11
+ class User < Striuct.new
12
+ member :id, Integer
13
+ member :address, /\A((\w+) ?)+\z/
14
+ member :age, (20..140)
15
+ member :name, /\A\w+\z/, /\A\w+ \w+\z/
16
+ end
17
+
18
+ # pass
19
+ user = User.new 128381, 'Tokyo Japan', 20
20
+ debug user
21
+
22
+ # pass
23
+ user.age = 30
24
+ user.name = 'taro yamada'
25
+ debug user
26
+
27
+ # fail (Exception Striuct::ConditionError)
28
+ begin
29
+ user[:id] = 10.0
30
+ rescue
31
+ debug $!
32
+ end
33
+
34
+ begin
35
+ user[1] = 'Tokyo-to'
36
+ rescue
37
+ debug $!
38
+ end
39
+
40
+ begin
41
+ user.age = 19
42
+ rescue
43
+ debug $!
44
+ end
45
+
46
+ begin
47
+ user.name = nil
48
+ rescue
49
+ debug $!
50
+ end
51
+
52
+ #* but, linked objects are able to clash
53
+ debug user
54
+ debug user.strict?
55
+ debug user
56
+ debug user.strict?
57
+
58
+ # more detail checker do you need, you can use functional object.
59
+ module Game
60
+ class Character
61
+ end
62
+
63
+ class DB < Striuct.new
64
+ member :monsters, ->monsters{(monsters - characters).empty?}
65
+ member :characters, ->characters{characters.all?{|c|c.kind_of? Character}}
66
+ end
67
+
68
+ monster = Character.new
69
+ db = DB.new
70
+
71
+ begin
72
+ db.characters = [1, 2]
73
+ rescue
74
+ debug $!
75
+ end
76
+
77
+ db.characters = [monster, Character.new]
78
+ debug db
79
+
80
+ begin
81
+ db.monsters = [:dummy]
82
+ rescue
83
+ debug $!
84
+ end
85
+
86
+ db.monsters = [monster]
87
+ debug db
88
+ end
89
+
90
+ # "inference", check under first passed object's class
91
+ class FlexibleContainer < Striuct.new
92
+ member :anything, inference
93
+ member :number, inference, Numeric
94
+ end
95
+
96
+ fc1, fc2 = FlexibleContainer.new, FlexibleContainer.new
97
+ fc1.anything = 'str'
98
+ debug fc1
99
+ begin
100
+ fc1.anything = :sym
101
+ rescue
102
+ debug $!
103
+ end
104
+
105
+ begin
106
+ fc2.anything = :sym
107
+ rescue
108
+ debug $!
109
+ end
110
+
111
+ fc2.anything = 'string too'
112
+
113
+ debug fc2
114
+
115
+ begin
116
+ fc1.number = 'str'
117
+ rescue
118
+ debug $!
119
+ end
120
+
121
+ fc1.number = 1.0
122
+ debug fc1
123
+
124
+ begin
125
+ fc2.number = 1
126
+ rescue
127
+ debug $!
128
+ end
129
+
130
+
131
+ # with flavor for type cast
132
+ class User2 < Striuct.new
133
+ member :age, /\A\d+\z/, Numeric do |arg|
134
+ Integer arg
135
+ end
136
+
137
+ member :name, ->v{v.respond_to? :to_str} do |v|
138
+ v.to_str.to_sym
139
+ end
140
+ end
141
+
142
+ user2 = User2.new
143
+ user2.age = 9
144
+ debug user2
145
+
146
+ user2.age = 10.1
147
+ debug user2
148
+
149
+ user2.age = '10'
150
+ debug user2
151
+
152
+ begin
153
+ user2.name = 10
154
+ rescue
155
+ debug $!
156
+ end
157
+
158
+ user2.name = 's'
159
+ debug user2.class
160
+
161
+ # use default value
162
+ class User3 < Striuct.new
163
+ member :lank, Fixnum
164
+ default :lank, 3
165
+ member :name
166
+ end
167
+
168
+ user3 = User3.new
169
+ user3
170
+ debug user3
171
+
172
+ # Standard Struct always define "nil is default". ...realy?
173
+ debug user3.assign?(:name)
174
+ user3.name = nil
175
+ debug user3.assign?(:name)
176
+
177
+ # Standard Struct no check member name.
178
+ NoGuard = Struct.new :__send__, :'? !'
179
+ noguard = NoGuard.new false
180
+ debug noguard.__send__
181
+ debug noguard.methods.include?(:'? !') # lost!!
182
+
183
+ # Striuct provides safety levels for naming.
184
+ class SafetyNaming < Striuct.new
185
+ begin
186
+ member :__send__
187
+ rescue
188
+ debug $!
189
+ end
190
+
191
+ begin
192
+ member :'? !'
193
+ rescue
194
+ debug $!
195
+ end
196
+
197
+ # set lower
198
+ protect_level :struct
199
+
200
+ member :__send__, :'? !'
201
+ end
202
+
203
+
204
+ # and keeping Struct's good interface
205
+ Sth1 = Striuct.new :id, :last_name, :family_name, :address, :age
206
+
207
+ debug Sth1.new
208
+
209
+ Sth2 = Striuct.new do
210
+ def my_special_method
211
+ end
212
+ end
213
+
214
+ debug Sth2.new.respond_to?(:my_special_method)
@@ -0,0 +1,26 @@
1
+ class Striuct
2
+
3
+
4
+ # @author Kenichi Kamiya
5
+ module ClassUtil
6
+
7
+ private
8
+
9
+ # @macro delegate_class_method
10
+ def delegate_class_method(name)
11
+ define_method name do |*args, &block|
12
+ self.class.__send__ name, *args, &block
13
+ end
14
+ end
15
+
16
+ # @macro delegate_class_methods
17
+ def delegate_class_methods(*names)
18
+ raise ArgumentError, 'wrong number of argument 0 for 1+' unless names.length >= 1
19
+
20
+ names.each{|name|delegate_class_method name}
21
+ end
22
+
23
+ end
24
+
25
+
26
+ end
@@ -0,0 +1,56 @@
1
+ # Copyright (C) 2011 Kenichi Kamiya
2
+
3
+ require_relative 'subclass_eigen'
4
+ require_relative 'subclass'
5
+
6
+ # @author Kenichi Kamiya
7
+ class Striuct
8
+
9
+ class ConditionError < ArgumentError; end
10
+
11
+ class << self
12
+ alias_method :new_instance, :new
13
+ private :new_instance
14
+
15
+ # @param [Symbol, String] *names
16
+ # @return [Class] - with Subclass, Subclass:Eigen
17
+ def new(*names, &block)
18
+ # warning for Ruby's Struct.new user
19
+ arg1 = names.first
20
+ if arg1.instance_of?(String) and /\A[A-Z]/ =~ arg1
21
+ warn "no define constant #{arg1}"
22
+ end
23
+
24
+ Class.new self do
25
+ names.each do |name|
26
+ member name
27
+ end
28
+
29
+ class_eval(&block) if block_given?
30
+ end
31
+ end
32
+
33
+ # @yieldreturn [Class] (see Striuct.new) - reject floating class
34
+ # @return [void]
35
+ def define(&block)
36
+ raise ArgumentError, 'must with block' unless block_given?
37
+
38
+ new(&block).tap do |subclass|
39
+ subclass.instance_eval do
40
+ raise 'not yet finished' if members.empty?
41
+ close
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def inherited(subclass)
49
+ subclass.class_eval do
50
+ extend Subclass::Eigen
51
+ include Subclass
52
+ end
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,95 @@
1
+ class Striuct
2
+
3
+ # @author Kenichi Kamiya
4
+ module StructExtension
5
+
6
+ module Eigen
7
+ def has_member?(name)
8
+ members.include? name
9
+ end
10
+
11
+ alias_method :member?, :has_member?
12
+ alias_method :has_key?, :has_member?
13
+ alias_method :key?, :has_key?
14
+
15
+ def has_condition?(name)
16
+ false
17
+ end
18
+
19
+ alias_method :restrict?, :has_condition?
20
+
21
+ def conditionable?(condition)
22
+ false
23
+ end
24
+
25
+ def sufficent?(name, value)
26
+ true
27
+ end
28
+
29
+ alias_method :accept?, :sufficent?
30
+
31
+ def has_default?(name)
32
+ false
33
+ end
34
+
35
+ def cname?(name)
36
+ [Symbol, String].any?{|klass|name.instance_of? klass}
37
+ end
38
+
39
+ def has_flavor?(name)
40
+ false
41
+ end
42
+
43
+ # @return [Struct]
44
+ def load_pairs(pairs)
45
+ raise TypeError, 'no pairs object' unless pairs.respond_to? :each_pair
46
+
47
+ new.tap do |r|
48
+ pairs.each_pair do |name, value|
49
+ if member? name
50
+ r[name] = value
51
+ else
52
+ raise ArgumentError, " #{name} is not our member"
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ def define(lock=false)
59
+ raise ArgumentError unless lock.equal?(false)
60
+
61
+ new.tap{|instance|yield instance}
62
+ end
63
+
64
+ # @return [StrictStruct]
65
+ def to_strict
66
+ StrictStruct.new(*members)
67
+ end
68
+
69
+ def closed?
70
+ true
71
+ end
72
+ end
73
+
74
+ def assign?(name)
75
+ ! self[name].nil?
76
+ end
77
+
78
+ def strict?
79
+ false
80
+ end
81
+
82
+ def secure?
83
+ false
84
+ end
85
+
86
+ end
87
+
88
+
89
+ end
90
+
91
+
92
+ class Struct
93
+ extend Striuct::StructExtension::Eigen
94
+ include Striuct::StructExtension
95
+ end
@@ -0,0 +1,262 @@
1
+ require_relative 'classutil'
2
+
3
+ class Striuct
4
+
5
+
6
+ # @author Kenichi Kamiya
7
+ module Subclass
8
+
9
+ extend ClassUtil
10
+ include Enumerable
11
+
12
+ def initialize(*values)
13
+ @db = {}
14
+
15
+ if values.size <= size
16
+ values.each_with_index do |v, idx|
17
+ self[idx] = v
18
+ end
19
+
20
+ excess = members.last(size - values.size)
21
+
22
+ excess.each do |name|
23
+ self[name] = default_for name if has_default? name
24
+ end
25
+ else
26
+ raise ArgumentError, "struct size differs (max: #{size})"
27
+ end
28
+ end
29
+
30
+ # @return [Boolean]
31
+ def ==(other)
32
+ if self.class.equal? other.class
33
+ each_pair.all?{|k, v|v == other[k]}
34
+ else
35
+ false
36
+ end
37
+ end
38
+
39
+ def eql?(other)
40
+ if self.class.equal? other.class
41
+ each_pair.all?{|k, v|v.eql? other[k]}
42
+ else
43
+ false
44
+ end
45
+ end
46
+
47
+ # @return [Integer]
48
+ def hash
49
+ values.map(&:hash).hash
50
+ end
51
+
52
+ # @return [String]
53
+ def inspect
54
+ "#<#{self.class} (StrictStruct)".tap do |s|
55
+ each_pair do |name, value|
56
+ suffix = (has_default?(name) && default?(name)) ? '(default)' : nil
57
+ s << " #{name}=#{value.inspect}#{suffix}"
58
+ end
59
+
60
+ s << ">"
61
+ end
62
+ end
63
+
64
+ # @return [String]
65
+ def to_s
66
+ "#<struct #{self.class}".tap do |s|
67
+ each_pair do |name, value|
68
+ s << " #{name}=#{value.inspect}"
69
+ end
70
+
71
+ s << '>'
72
+ end
73
+ end
74
+
75
+ delegate_class_methods(
76
+ :members, :keys, :has_member?, :member?, :has_key?, :key?, :length,
77
+ :size, :keyable_for, :restrict?, :has_default?, :default_for,
78
+ :names, :has_flavor?, :flavor_for, :has_conditions?, :inference?
79
+ )
80
+
81
+ private :keyable_for, :flavor_for
82
+
83
+ # @param [Symbol, String, Fixnum] key
84
+ def [](key)
85
+ __subscript__(key){|name|__get__ name}
86
+ end
87
+
88
+ # @param [Symbol, String, Fixnum] key
89
+ # @param [Object] value
90
+ def []=(key, value)
91
+ __subscript__(key){|name|__set__ name, value}
92
+ end
93
+
94
+ # @yield [name]
95
+ # @yieldparam [Symbol] name - sequential under defined
96
+ # @yieldreturn [self]
97
+ # @return [Enumerator]
98
+ def each_name
99
+ return to_enum(__method__) unless block_given?
100
+ self.class.each_name{|name|yield name}
101
+ self
102
+ end
103
+
104
+ alias_method :each_member, :each_name
105
+ alias_method :each_key, :each_name
106
+
107
+ # @yield [value]
108
+ # @yieldparam [Object] value - sequential under defined (see #each_name)
109
+ # @yieldreturn [self]
110
+ # @return [Enumerator]
111
+ def each_value
112
+ return to_enum(__method__) unless block_given?
113
+ each_member{|member|yield self[member]}
114
+ end
115
+
116
+ alias_method :each, :each_value
117
+
118
+ # @yield [name, value]
119
+ # @yieldparam [Symbol] name (see #each_name)
120
+ # @yieldparam [Object] value (see #each_value)
121
+ # @yieldreturn [self]
122
+ # @return [Enumerator]
123
+ def each_pair
124
+ return to_enum(__method__) unless block_given?
125
+ each_name{|name|yield name, self[name]}
126
+ end
127
+
128
+ # @return [Array]
129
+ def values
130
+ [].tap do |r|
131
+ each_value do |v|
132
+ r << v
133
+ end
134
+ end
135
+ end
136
+
137
+ alias_method :to_a, :values
138
+
139
+ # @param [Fixnum, Range] *keys
140
+ # @return [Array]
141
+ def values_at(*keys)
142
+ [].tap do |r|
143
+ keys.each do |key|
144
+ case key
145
+ when Fixnum
146
+ r << self[key]
147
+ when Range
148
+ key.each do |n|
149
+ r << self[n]
150
+ end
151
+ else
152
+ raise TypeError
153
+ end
154
+ end
155
+ end
156
+ end
157
+
158
+ # @param [Symbol, String] name
159
+ def assign?(name)
160
+ name = keyable_for name
161
+ raise NameError unless member? name
162
+
163
+ @db.has_key? name
164
+ end
165
+
166
+ # @param [Symbol, String] name
167
+ def unassign(name)
168
+ raise "can't modify frozen #{self.class}" if frozen?
169
+ name = keyable_for name
170
+ raise NameError unless member? name
171
+
172
+ @db.delete name
173
+ end
174
+
175
+ # @param [Symbol, String] name
176
+ # @param [Object] *values - no argument and use own
177
+ def sufficent?(name, value=self[name])
178
+ self.class.__send__(__method__, name, value, self)
179
+ end
180
+
181
+ alias_method :accept?, :sufficent?
182
+
183
+ def strict?
184
+ each_pair.all?{|name, value|self.class.sufficent? name, value}
185
+ end
186
+
187
+ def secure?
188
+ frozen? && self.class.closed? && strict?
189
+ end
190
+
191
+ # @return [self]
192
+ def freeze
193
+ @db.freeze
194
+ super
195
+ end
196
+
197
+ # @param [Symbol, String] name
198
+ def default?(name)
199
+ default_for(name) == self[name]
200
+ end
201
+
202
+ private
203
+
204
+ def initialize_copy(org)
205
+ @db = @db.clone
206
+ end
207
+
208
+ def __get__(name)
209
+ name = keyable_for name
210
+ raise NameError unless member? name
211
+
212
+ @db[name]
213
+ end
214
+
215
+ def __set__(name, value)
216
+ raise "can't modify frozen #{self.class}" if frozen?
217
+ name = keyable_for name
218
+ raise NameError unless member? name
219
+
220
+ if accept? name, value
221
+ if has_flavor? name
222
+ value = instance_exec value, &flavor_for(name)
223
+ end
224
+
225
+ if inference? name
226
+ self.class.__send__ :__found_family__!, name, value
227
+ end
228
+
229
+ @db[name] = value
230
+ else
231
+ raise ConditionError, 'deficent value for all conditions'
232
+ end
233
+ end
234
+
235
+ alias_method :assign, :__set__
236
+ public :assign
237
+
238
+ def __subscript__(key)
239
+ case key
240
+ when Symbol, String
241
+ name = keyable_for key
242
+ if member? name
243
+ yield name
244
+ else
245
+ raise NameError
246
+ end
247
+ when Fixnum
248
+ if name = members[key]
249
+ yield name
250
+ else
251
+ raise IndexError
252
+ end
253
+ else
254
+ raise ArgumentError
255
+ end
256
+ end
257
+
258
+
259
+ end
260
+
261
+
262
+ end