main 0.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,35 +1,30 @@
1
1
  #
2
2
  # The ArrayFields module implements methods which allow an Array to be indexed
3
- # by String or Symbol. It is not required to manually use this module to extend
4
- # Array's - they are auto-extended when Array#fields= is called
3
+ # by String or Symbol. It is not required to manually use this module to
4
+ # extend Arrays - they are auto-extended on a per-object basis when
5
+ # Array#fields= is called
5
6
  #
6
7
  module ArrayFields
7
- #{{{
8
- VERSION = '3.6.0'
8
+ self::VERSION = '4.5.0' unless defined? self::VERSION
9
+ def self.version() VERSION end
9
10
  #
10
11
  # multiton cache of fields - wraps fields and fieldpos map to save memory
11
12
  #
12
13
  class FieldSet
13
- #{{{
14
14
  class << self
15
- #{{{
16
15
  def new fields
17
- #{{{
18
- @sets ||= {}
19
- obj = @sets[fields]
20
- unless obj
21
- obj = super
22
- @sets[fields] = obj
23
- end
24
- obj
25
- #}}}
16
+ @sets[fields] ||= super
17
+ end
18
+ def init_sets
19
+ @sets = {}
26
20
  end
27
- #}}}
28
21
  end
22
+
23
+ init_sets
24
+
29
25
  attr :fields
30
26
  attr :fieldpos
31
27
  def initialize fields
32
- #{{{
33
28
  raise ArgumentError, "<#{ fields.inspect }> not inject-able" unless
34
29
  fields.respond_to? :inject
35
30
 
@@ -43,25 +38,29 @@
43
38
  end
44
39
 
45
40
  @fields = fields
46
- #}}}
47
41
  end
48
42
  def pos f
49
- #{{{
50
43
  return @fieldpos[f] if @fieldpos.has_key? f
51
44
  f = f.to_s
52
45
  return @fieldpos[f] if @fieldpos.has_key? f
53
46
  f = f.intern
54
47
  return @fieldpos[f] if @fieldpos.has_key? f
55
48
  nil
56
- #}}}
57
49
  end
58
- #}}}
59
50
  end
60
51
  #
61
52
  # methods redefined to work with fields as well as numeric indexes
62
53
  #
63
- def [](idx, *args)
64
- #{{{
54
+ def [] idx, *args
55
+ if @fieldset and (String === idx or Symbol === idx)
56
+ pos = @fieldset.pos idx
57
+ return nil unless pos
58
+ super(pos, *args)
59
+ else
60
+ super
61
+ end
62
+ end
63
+ def slice idx, *args
65
64
  if @fieldset and (String === idx or Symbol === idx)
66
65
  pos = @fieldset.pos idx
67
66
  return nil unless pos
@@ -69,11 +68,9 @@
69
68
  else
70
69
  super
71
70
  end
72
- #}}}
73
71
  end
74
- alias slice []
72
+
75
73
  def []=(idx, *args)
76
- #{{{
77
74
  if @fieldset and (String === idx or Symbol === idx)
78
75
  pos = @fieldset.pos idx
79
76
  unless pos
@@ -84,10 +81,8 @@
84
81
  else
85
82
  super
86
83
  end
87
- #}}}
88
84
  end
89
85
  def at idx
90
- #{{{
91
86
  if @fieldset and (String === idx or Symbol === idx)
92
87
  pos = @fieldset.pos idx
93
88
  return nil unless pos
@@ -95,10 +90,8 @@
95
90
  else
96
91
  super
97
92
  end
98
- #}}}
99
93
  end
100
94
  def delete_at idx
101
- #{{{
102
95
  if @fieldset and (String === idx or Symbol === idx)
103
96
  pos = @fieldset.pos idx
104
97
  return nil unless pos
@@ -106,10 +99,8 @@
106
99
  else
107
100
  super
108
101
  end
109
- #}}}
110
102
  end
111
103
  def fill(obj, *args)
112
- #{{{
113
104
  idx = args.first
114
105
  if idx and @fieldset and (String === idx or Symbol === idx)
115
106
  idx = args.shift
@@ -118,67 +109,69 @@
118
109
  else
119
110
  super
120
111
  end
121
- #}}}
122
112
  end
113
+
123
114
  def values_at(*idxs)
124
- #{{{
125
115
  idxs.flatten!
126
116
  if @fieldset
127
117
  idxs.map!{|i| (String === i or Symbol === i) ? @fieldset.pos(i) : i}
128
118
  end
129
119
  super(*idxs)
130
- #}}}
131
120
  end
132
- alias indices values_at
133
- alias indexes values_at
121
+ def indices(*idxs)
122
+ idxs.flatten!
123
+ if @fieldset
124
+ idxs.map!{|i| (String === i or Symbol === i) ? @fieldset.pos(i) : i}
125
+ end
126
+ super(*idxs)
127
+ end
128
+ def indexes(*idxs)
129
+ idxs.flatten!
130
+ if @fieldset
131
+ idxs.map!{|i| (String === i or Symbol === i) ? @fieldset.pos(i) : i}
132
+ end
133
+ super(*idxs)
134
+ end
135
+
134
136
  def slice!(*args)
135
- #{{{
136
137
  ret = self[*args]
137
138
  self[*args] = nil
138
139
  ret
139
- #}}}
140
140
  end
141
141
  def each_with_field
142
- #{{{
143
142
  each_with_index do |elem, i|
144
143
  yield elem, @fieldset.fields[i]
145
144
  end
146
- #}}}
147
145
  end
148
146
  #
149
147
  # methods which give a hash-like interface
150
148
  #
151
149
  def each_pair
152
- #{{{
153
150
  each_with_index do |elem, i|
154
151
  yield @fieldset.fields[i], elem
155
152
  end
156
- #}}}
157
153
  end
158
154
  def each_key
159
- #{{{
160
155
  @fieldset.each{|field| yield field}
161
- #}}}
162
156
  end
163
- def each_value(*args, &block)
164
- #{{{
165
- each(*args, &block)
166
- #}}}
157
+ def each_value *args, &block
158
+ each *args, &block
167
159
  end
168
160
  def fetch key
169
- #{{{
170
161
  self[key] or raise IndexError, 'key not found'
171
- #}}}
172
162
  end
163
+
173
164
  def has_key? key
174
- #{{{
175
165
  @fieldset.fields.include? key
176
- #}}}
177
166
  end
178
- alias member? has_key?
179
- alias key? has_key?
167
+ def member? key
168
+ @fieldset.fields.include? key
169
+ end
170
+ def key? key
171
+ @fieldset.fields.include? key
172
+ end
173
+
180
174
  def has_value? value
181
- #{{{
182
175
  if respond_to? 'include?'
183
176
  self.include? value
184
177
  else
@@ -186,21 +179,24 @@
186
179
  each{|val| a << val}
187
180
  a.include? value
188
181
  end
189
- #}}}
190
182
  end
191
- alias value? has_value?
183
+ def value? value
184
+ if respond_to? 'include?'
185
+ self.include? value
186
+ else
187
+ a = []
188
+ each{|val| a << val}
189
+ a.include? value
190
+ end
191
+ end
192
+
192
193
  def keys
193
- #{{{
194
194
  fields
195
- #}}}
196
195
  end
197
196
  def store key, value
198
- #{{{
199
197
  self[key] = value
200
- #}}}
201
198
  end
202
199
  def values
203
- #{{{
204
200
  if respond_to? 'to_ary'
205
201
  self.to_ary
206
202
  else
@@ -208,10 +204,9 @@
208
204
  each{|val| a << val}
209
205
  a
210
206
  end
211
- #}}}
212
207
  end
208
+
213
209
  def to_hash
214
- #{{{
215
210
  if respond_to? 'to_ary'
216
211
  h = {}
217
212
  @fieldset.fields.zip(to_ary){|f,e| h[f] = e}
@@ -223,40 +218,81 @@
223
218
  @fieldset.fields.zip(a){|f,e| h[f] = e}
224
219
  h
225
220
  end
226
- #}}}
227
221
  end
228
- alias to_h to_hash
222
+ def to_h
223
+ if respond_to? 'to_ary'
224
+ h = {}
225
+ @fieldset.fields.zip(to_ary){|f,e| h[f] = e}
226
+ h
227
+ else
228
+ a = []
229
+ each{|val| a << val}
230
+ h = {}
231
+ @fieldset.fields.zip(a){|f,e| h[f] = e}
232
+ h
233
+ end
234
+ end
235
+
229
236
  def update other
230
- #--{{{
231
237
  other.each{|k,v| self[k] = v}
232
238
  to_hash
233
- #--}}}
234
239
  end
235
240
  def replace other
236
- #--{{{
237
241
  Hash === other ? update(other) : super
238
- #--}}}
239
242
  end
240
243
  def invert
241
- #--{{{
242
244
  to_hash.invert
243
- #--}}}
244
245
  end
245
- #}}}
246
+
247
+ def to_pairs
248
+ fields.zip values
249
+ end
250
+ alias_method 'pairs', 'to_pairs'
251
+
252
+ def copy
253
+ cp = clone
254
+ cp.fields = fields.clone
255
+ cp
256
+ end
257
+
258
+ alias_method 'dup', 'copy'
259
+ alias_method 'clone', 'copy'
260
+
261
+ def deepcopy
262
+ cp = Marshal.load(Marshal.dump(self))
263
+ cp.fields = Marshal.load(Marshal.dump(self.fields))
264
+ cp
265
+ end
246
266
  end
267
+ Arrayfields = ArrayFields
268
+
269
+ module Arrayfields
270
+ def self.new *pairs
271
+ pairs = pairs.map{|pair| Enumerable === pair ? pair.to_a : pair}.flatten
272
+ raise ArgumentError, "pairs must be evenly sized" unless(pairs.size % 2 == 0)
273
+ (( array = [] )).fields = []
274
+ 0.step(pairs.size - 2, 2) do |a|
275
+ b = a + 1
276
+ array[ pairs[a] ] = pairs[b]
277
+ end
278
+ array
279
+ end
280
+ def self.[] *pairs
281
+ new *pairs
282
+ end
283
+ end
284
+ def Arrayfields(*a, &b) Arrayfields.new(*a, &b) end
247
285
  #
248
286
  # Fieldable encapsulates methods in common for classes which may have their
249
- # fields set
287
+ # fields set and subsequently be auto-extended by ArrayFields
250
288
  #
251
289
  module Fieldable
252
- #{{{
253
290
  #
254
291
  # sets fields an dynamically extends this Array instance with methods for
255
292
  # keyword access
256
293
  #
257
294
  def fields= fields
258
- #{{{
259
- extend ArrayFields unless defined? @fieldset
295
+ extend ArrayFields unless ArrayFields === self
260
296
 
261
297
  @fieldset =
262
298
  if ArrayFields::FieldSet === fields
@@ -264,7 +300,6 @@
264
300
  else
265
301
  ArrayFields::FieldSet.new fields
266
302
  end
267
- #}}}
268
303
  end
269
304
  #
270
305
  # access to fieldset
@@ -274,19 +309,58 @@
274
309
  # access to field list
275
310
  #
276
311
  def fields
277
- #{{{
278
312
  @fieldset and @fieldset.fields
279
- #}}}
280
313
  end
281
- #}}}
282
314
  end
283
315
  #
284
- # The Array class is extened with a methods to allow keyword access
316
+ # Array instances are extened with two methods only: Fieldable#fields= and
317
+ # Fieldable#fields. only when Fieldable#fields= is called will the full set
318
+ # of ArrayFields methods auto-extend the Array instance. the Array class also
319
+ # has added a class generator when the fields are known apriori.
285
320
  #
286
321
  class Array
287
- #{{{
288
322
  include Fieldable
289
- #}}}
323
+
324
+ class << self
325
+ def struct *fields
326
+ fields = fields.flatten
327
+ Class.new(self) do
328
+ include ArrayFields
329
+ const_set :FIELDS, ArrayFields::FieldSet.new(fields)
330
+ fields.each do |field|
331
+ field = field.to_s
332
+ if field =~ %r/^[a-zA-Z_][a-zA-Z0-9_]*$/
333
+ begin
334
+ module_eval <<-code
335
+ def #{ field } *a
336
+ a.size == 0 ? self['#{ field }'] : (self.#{ field } = a.shift)
337
+ end
338
+ def #{ field }= value
339
+ self['#{ field }'] = value
340
+ end
341
+ code
342
+ rescue SyntaxError
343
+ :by_ignoring_it
344
+ end
345
+ end
346
+ end
347
+ def initialize *a, &b
348
+ super
349
+ ensure
350
+ @fieldset = self.class.const_get :FIELDS
351
+ end
352
+ def self.[] *elements
353
+ array = new
354
+ array.replace elements
355
+ array
356
+ end
357
+ end
358
+ end
359
+ def fields *fields, &block
360
+ (( array = new(&block) )).fields = fields.map{|x| Enumerable === x ? x.to_a : x}.flatten
361
+ array
362
+ end
363
+ end
290
364
  end
291
365
  #
292
366
  # proxy class that allows an array to be wrapped in a way that still allows #
@@ -300,48 +374,61 @@
300
374
  #
301
375
  #
302
376
  class FieldedArray
303
- #{{{
304
377
  include Fieldable
305
378
  class << self
306
-
307
379
  def [](*pairs)
308
- #{{{
309
380
  pairs.flatten!
310
- raise ArgumentError, "argument must be key/val paris" unless
311
- (pairs.size % 2 == 0 and pairs.size >= 2)
381
+ raise ArgumentError, "argument must be key/val pairs" unless
382
+ (pairs.size % 2 == 0)
312
383
  fields, elements = [], []
313
- #pairs.each do |f,e|
314
384
  while((f = pairs.shift) and (e = pairs.shift))
315
- raise ArgumentError, "field must be String or Symbol" unless
316
- (String === f or Symbol === f)
317
385
  fields << f and elements << e
318
386
  end
319
387
  new fields, elements
320
- #}}}
321
388
  end
322
-
323
389
  end
324
- def initialize fields, array
325
- #{{{
390
+ def initialize fields = [], array = []
326
391
  @a = array
327
392
  self.fields = fields
328
- #}}}
329
393
  end
330
394
  def method_missing(meth, *args, &block)
331
- #{{{
332
395
  @a.send(meth, *args, &block)
333
- #}}}
334
396
  end
335
397
  delegates =
336
- #{{{
337
398
  %w(
338
399
  to_s
339
400
  to_str
340
401
  inspect
341
402
  )
342
- #}}}
343
403
  delegates.each do |meth|
344
404
  class_eval "def #{ meth }(*a,&b); @a.#{ meth }(*a,&b);end"
345
405
  end
346
- #}}}
347
406
  end
407
+ Fieldedarray = FieldedArray
408
+
409
+ class PseudoHash < ::Array
410
+ class << self
411
+ def [](*pairs)
412
+ pairs.flatten!
413
+ raise ArgumentError, "argument must be key/val pairs" unless
414
+ (pairs.size % 2 == 0 and pairs.size >= 2)
415
+ keys, values = [], []
416
+ while((k = pairs.shift) and (v = pairs.shift))
417
+ keys << k and values << v
418
+ end
419
+ new keys, values
420
+ end
421
+ end
422
+ def initialize keys = [], values = []
423
+ self.fields = keys
424
+ self.replace values
425
+ end
426
+ def to_yaml opts = {}
427
+ YAML::quick_emit object_id, opts do |out|
428
+ out.map taguri, to_yaml_style do |map|
429
+ each_pair{|f,v| map.add f,v}
430
+ end
431
+ end
432
+ end
433
+ end
434
+ Pseudohash = PseudoHash