main 0.0.2 → 2.0.0

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.
@@ -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