arrayfields 3.6.0 → 3.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -1,72 +1,181 @@
1
- URLS:
1
+ NAME
2
+ arrayfields.rb
2
3
 
3
- - http://raa.ruby-lang.org/project/arrayfields/
4
- - http://www.codeforpeople.com/lib/ruby/arrayfields/
5
- - http://rubyforge.org/projects/arrayfields/
6
-
7
- SYNOPSIS:
8
-
9
- allow keyword access to arrays:
4
+ URIS
5
+ http://rubyforge.org/projects/arrayfields/
6
+ http://www.codeforpeople.com/lib/ruby/arrayfields/
7
+ http://raa.ruby-lang.org/project/arrayfields/
10
8
 
9
+ SYNOPSIS
11
10
  require 'arrayfields'
12
11
 
13
12
  fields = 'name', 'age'
14
- row = [ 'bob', 30 ]
15
-
16
- row.fields = fields
17
-
18
- row[ 'name' ] #=> 'bob'
19
- row.indices 'name', 'age' #=> [ 'bob', 30 ]
20
-
21
- assigning to un-named fields appends:
22
-
23
- stack = []
24
- stack.fields = %w(zero one)
25
- stack['zero'] = 'zero'
26
- stack['one'] = 'one'
27
- stack #=> [ 'zero', 'one' ]
28
-
29
- useful for database work:
30
-
31
- relation = pgconn.query sql
32
- relation.size #=> 65536
33
-
34
- # yikes! do we really want to re-construct a hash for for each tuple when
35
- # we already have Arrays?
36
-
37
- fields = %w(ssn name position)
38
- table.each{|tuple| tuple.fields = fields}
39
-
40
- tuples[34578]['ssn'] #=> 574865032
41
-
42
- LIST OF OVERRIDDEN METHODS:
43
-
44
- - Array#[]
45
- - Array#[]=
46
- - Array#at
47
- - Array#delete_at
48
- - Array#fill
49
- - Array#values_at
50
- - Array#indices
51
- - Array#indexes
52
- - Array#slice
53
- - Array#slice!
54
-
55
- LIST OF NEW Array METHODS:
56
-
57
- - Array#fields=
58
- - Array#each_with_field
59
-
60
- DOCS/USAGE/SAMPLE:
61
-
62
- - lib/arrayfields.rb
63
- - test/arrayfields.rb
64
-
65
- AUTHOR:
66
-
67
- ara.t.howard@noaa.gov
68
-
69
- HISTORY:
13
+ a = [ 'zaphod', 42 ]
14
+
15
+ a.fields = fields
16
+
17
+ a[ 'name' ] #=> 'zaphod'
18
+ a[ :name ] #=> 'zaphod'
19
+ a.indices 'name', 'age' #=> [ 'zaphod', 42 ]
20
+
21
+ DESCRIPTION
22
+ allow keyword access to array instances. arrayfields works by adding only a
23
+ few methods to arrays, namely #fields= and fields, but the #fields= method
24
+ is hooked to extend arrays on a per object basis. in otherwords __only__
25
+ those arrays whose fields are set will have auto-magical keyword access
26
+ bestowed on them - all other arrays remain unaffected. arrays with keyword
27
+ access require much less memory when compared to hashes/objects and yet
28
+ still provide fast lookup.
29
+
30
+ LIST OF OVERRIDDEN METHODS
31
+ Array#[]
32
+ Array#slice
33
+ Array#[]=
34
+ Array#at
35
+ Array#delete_at
36
+ Array#fill
37
+ Array#values_at
38
+ Array#indices
39
+ Array#indexes
40
+ Array#slice!
41
+
42
+ LIST OF HASH-LIKE METHODS
43
+ Array#each_with_field
44
+ Array#each_pair
45
+ Array#each_key
46
+ Array#each_value
47
+ Array#fetch
48
+ Array#has_key?
49
+ Array#member?
50
+ Array#key?
51
+ Array#has_value?
52
+ Array#value?
53
+ Array#keys
54
+ Array#store
55
+ Array#values
56
+ Array#to_hash
57
+ Array#to_h
58
+ Array#update
59
+ Array#replace
60
+ Array#invert
61
+
62
+ LIST OF ADDED Array METHODS
63
+ Array#fields=
64
+ Array#fields
65
+
66
+ LIST OF ADDED Array CLASS METHODS
67
+ Array.fields/Array.struct
68
+
69
+ SAMPLES
70
+
71
+ <========< sample/a.rb >========>
72
+
73
+ ~ > cat sample/a.rb
74
+
75
+ require 'arrayfields'
76
+ #
77
+ # the class Array has only a few added method, one is for setting the fields,
78
+ # when the fields are set for an array THIS INSTANCE ONLY will be modified to
79
+ # allow keyword access. other arrays will not be affected!
80
+ #
81
+ a = [0,1,2]
82
+ fields = ['zero', 'one', 'two']
83
+ a.fields = fields # ONLY the Array 'a' is affected!
84
+ #
85
+ # keyword access is now allowed for many methods
86
+ #
87
+ p a['zero'] #=> 0
88
+ p a['one'] #=> 1
89
+ p a['two'] #=> 2
90
+ p a.at('one') #=> 1
91
+ p a.values_at('zero', 'two') #=> [0, 2]
92
+ #
93
+ # assigmnet is allowed
94
+ #
95
+ a['zero'] = 42
96
+ p a['zero'] #=> 0
97
+ a['zero'] = 0
98
+ #
99
+ # assignment to non-fields results in the element being appended and the field
100
+ # being added for future use (also appended)
101
+ #
102
+ p(a.fields.join(',')) #=> "zero, one, two"
103
+ p a['three'] #=> nil
104
+ a['three'] = 3
105
+ p(a.fields.join(',')) #=> "zero, one, two, three"
106
+ p a['three'] #=> 3
107
+ #
108
+ # other detructive methods are also keyword enabled
109
+ #
110
+ a.fill 42, 'zero', len = a.size
111
+ p(a.values_at(a.fields)) #=> [42, 42, 42, 42]
112
+ a.replace [0,1,2,3]
113
+
114
+ a.slice! 'two', 2
115
+ p a #=> [0,1]
116
+
117
+ ~ > ruby sample/a.rb
118
+
119
+ 0
120
+ 1
121
+ 2
122
+ 1
123
+ [0, 2]
124
+ 42
125
+ "zero,one,two"
126
+ nil
127
+ "zero,one,two,three"
128
+ 3
129
+ [42, 42, 42, 42]
130
+ [0, 1]
131
+
132
+
133
+ <========< sample/b.rb >========>
134
+
135
+ ~ > cat sample/b.rb
136
+
137
+ require 'arrayfields'
138
+ #
139
+ # the struct/fields factory method can be used in much the same way as ruby's
140
+ # own struct generators and is useful when the fields for a set of arrays is
141
+ # known apriori
142
+ #
143
+ c = Array.fields :a, :b, :c # same as Array.struct
144
+ a = c.new [42, nil, nil]
145
+ a[:c] = 42
146
+ p a #=> [42, nil, 42]
147
+ #
148
+ # of course we can append too
149
+ #
150
+ a[:d] = 42.0
151
+ p a[:d] #=> 42.0
152
+ p a #=> [42, nil, 42, 42.0]
153
+
154
+ ~ > ruby sample/b.rb
155
+
156
+ [42, nil, 42]
157
+ 42.0
158
+ [42, nil, 42, 42.0]
159
+
160
+
161
+ AUTHOR
162
+ ara.t.howard@gmail.com
163
+
164
+ HISTORY
165
+ 3.7.0:
166
+ - cleaned up multiton pattern in ArrayFields::FieldSet
167
+ - mods for ruby 1.8.6
168
+ - added PseudoHash class
169
+ - added Array.struct/fields class generator
170
+
171
+ 3.6.0:
172
+ - made string/symbol keys interchangeable
173
+
174
+ list = [0, 1, 2]
175
+ list.fields = %w( a b c )
176
+ p list['a'] #=> 0
177
+ p list[:a] #=> 0
178
+
70
179
 
71
180
  3.5.0:
72
181
  - added more hash-like methods
@@ -0,0 +1,147 @@
1
+ NAME
2
+ arrayfields.rb
3
+
4
+ URIS
5
+ http://rubyforge.org/projects/arrayfields/
6
+ http://www.codeforpeople.com/lib/ruby/arrayfields/
7
+ http://raa.ruby-lang.org/project/arrayfields/
8
+
9
+ SYNOPSIS
10
+ require 'arrayfields'
11
+
12
+ fields = 'name', 'age'
13
+ a = [ 'zaphod', 42 ]
14
+
15
+ a.fields = fields
16
+
17
+ a[ 'name' ] #=> 'zaphod'
18
+ a[ :name ] #=> 'zaphod'
19
+ a.indices 'name', 'age' #=> [ 'zaphod', 42 ]
20
+
21
+ DESCRIPTION
22
+ allow keyword access to array instances. arrayfields works by adding only a
23
+ few methods to arrays, namely #fields= and fields, but the #fields= method
24
+ is hooked to extend arrays on a per object basis. in otherwords __only__
25
+ those arrays whose fields are set will have auto-magical keyword access
26
+ bestowed on them - all other arrays remain unaffected. arrays with keyword
27
+ access require much less memory when compared to hashes/objects and yet
28
+ still provide fast lookup.
29
+
30
+ LIST OF OVERRIDDEN METHODS
31
+ Array#[]
32
+ Array#slice
33
+ Array#[]=
34
+ Array#at
35
+ Array#delete_at
36
+ Array#fill
37
+ Array#values_at
38
+ Array#indices
39
+ Array#indexes
40
+ Array#slice!
41
+
42
+ LIST OF HASH-LIKE METHODS
43
+ Array#each_with_field
44
+ Array#each_pair
45
+ Array#each_key
46
+ Array#each_value
47
+ Array#fetch
48
+ Array#has_key?
49
+ Array#member?
50
+ Array#key?
51
+ Array#has_value?
52
+ Array#value?
53
+ Array#keys
54
+ Array#store
55
+ Array#values
56
+ Array#to_hash
57
+ Array#to_h
58
+ Array#update
59
+ Array#replace
60
+ Array#invert
61
+
62
+ LIST OF ADDED Array METHODS
63
+ Array#fields=
64
+ Array#fields
65
+
66
+ LIST OF ADDED Array CLASS METHODS
67
+ Array.fields/Array.struct
68
+
69
+ SAMPLES
70
+ @samples
71
+
72
+ AUTHOR
73
+ ara.t.howard@gmail.com
74
+
75
+ HISTORY
76
+ 3.7.0:
77
+ - multiton pattern clean up, thanks gavin kistner!
78
+ - mods for ruby 1.8.6 (alias bug in 1.8.6 i think)
79
+ - added PseudoHash class
80
+ - added Array.struct/fields class generator
81
+
82
+ 3.6.0:
83
+ - made string/symbol keys interchangeable
84
+
85
+ list = [0, 1, 2]
86
+ list.fields = %w( a b c )
87
+ p list['a'] #=> 0
88
+ p list[:a] #=> 0
89
+
90
+
91
+ 3.5.0:
92
+ - added more hash-like methods
93
+ - update
94
+ - replace
95
+ - invert
96
+
97
+ 3.4.0:
98
+ - added FieldedArray[] ctor
99
+ - added methods to make Arrays with fields set behave more closely to Hashes
100
+ - each_pair
101
+ - each_key
102
+ - each_value
103
+ - fetch
104
+ - has_key?
105
+ - member?
106
+ - key?
107
+ - has_value?
108
+ - value?
109
+ - keys?
110
+ - store
111
+ - values
112
+
113
+ 3.3.0:
114
+ - added gemspec file - thnx Assaph Mehr
115
+ - added FieldedArray proxy class which minimizes modifications to class
116
+ Array and allow ArrayFields to work (potientially) other arraylike object.
117
+ thnks Sean O'Dell
118
+ - added ArrayFields#to_hash method - this seems like an obvious one to add!
119
+ - remedied bug where using append feature of assigning with unknow field
120
+ appedended but did not append to acutal fields
121
+ - added samples
122
+ - created rubyforge accnt @ http://rubyforge.org/projects/arrayfields/
123
+
124
+ 3.2.0:
125
+ - precedence fix in many methods - thnx. nobu
126
+ - test for #slice! were not being run - corrected
127
+ - added test for appeding via "a['new_field'] = 42"
128
+
129
+ 3.1.0:
130
+ - added FieldSet class to reduce ram - thnx. Kirk Haines for profiliing
131
+ memory and prompting this change
132
+
133
+ - interface changed every so slightly so
134
+
135
+ a.fields = 'a', 'b', 'c'
136
+
137
+ is not allowed. use
138
+
139
+ a.fields = %w(a b c)
140
+
141
+ or
142
+
143
+ a.fields = ['a', 'b', 'c']
144
+
145
+
146
+ 3.0.0:
147
+ - added unit tests
data/gemspec.rb CHANGED
@@ -1,8 +1,10 @@
1
+
1
2
  lib, version = File::basename(File::dirname(File::expand_path(__FILE__))).split %r/-/, 2
2
3
 
3
4
  require 'rubygems'
4
5
 
5
6
  Gem::Specification::new do |spec|
7
+ $VERBOSE = nil
6
8
  spec.name = lib
7
9
  spec.version = version
8
10
  spec.platform = Gem::Platform::RUBY
@@ -17,6 +19,8 @@ Gem::Specification::new do |spec|
17
19
  spec.has_rdoc = File::exist? "doc"
18
20
  spec.test_suite_file = "test/#{ lib }.rb" if File::directory? "test"
19
21
 
22
+ spec.extensions << "extconf.rb" if File::exists? "extconf.rb"
23
+
20
24
  spec.author = "Ara T. Howard"
21
25
  spec.email = "ara.t.howard@noaa.gov"
22
26
  spec.homepage = "http://codeforpeople.com/lib/ruby/#{ lib }/"
@@ -0,0 +1,32 @@
1
+ require 'pathname'
2
+
3
+ $VERBOSE=nil
4
+
5
+ def indent s, n = 2
6
+ ws = ' ' * n
7
+ s.gsub %r/^/, ws
8
+ end
9
+
10
+ template = IO::read 'README.tmpl'
11
+
12
+ samples = ''
13
+ prompt = '~ > '
14
+
15
+ Dir['sample*/*'].sort.each do |sample|
16
+ samples << "\n" << " <========< #{ sample } >========>" << "\n\n"
17
+
18
+ cmd = "cat #{ sample }"
19
+ samples << indent(prompt + cmd, 2) << "\n\n"
20
+ samples << indent(`#{ cmd }`, 4) << "\n"
21
+
22
+ cmd = "ruby #{ sample }"
23
+ samples << indent(prompt + cmd, 2) << "\n\n"
24
+
25
+ cmd = "ruby -Ilib #{ sample }"
26
+ samples << indent(`#{ cmd } 2>&1`, 4) << "\n"
27
+ end
28
+
29
+ #samples.gsub! %r/^/, ' '
30
+
31
+ readme = template.gsub %r/^\s*@samples\s*$/, samples
32
+ print readme
@@ -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
+ VERSION = '3.7.0' unless defined? 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,20 @@
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
65
55
  if @fieldset and (String === idx or Symbol === idx)
66
56
  pos = @fieldset.pos idx
67
57
  return nil unless pos
@@ -69,11 +59,18 @@
69
59
  else
70
60
  super
71
61
  end
72
- #}}}
73
62
  end
74
- alias slice []
63
+ def slice idx, *args
64
+ if @fieldset and (String === idx or Symbol === idx)
65
+ pos = @fieldset.pos idx
66
+ return nil unless pos
67
+ super(pos, *args)
68
+ else
69
+ super
70
+ end
71
+ end
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
157
  def each_value(*args, &block)
164
- #{{{
165
158
  each(*args, &block)
166
- #}}}
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,39 +218,42 @@
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
246
  end
247
247
  #
248
248
  # Fieldable encapsulates methods in common for classes which may have their
249
- # fields set
249
+ # fields set and subsequently be auto-extended by ArrayFields
250
250
  #
251
251
  module Fieldable
252
- #{{{
253
252
  #
254
253
  # sets fields an dynamically extends this Array instance with methods for
255
254
  # keyword access
256
255
  #
257
256
  def fields= fields
258
- #{{{
259
257
  extend ArrayFields unless defined? @fieldset
260
258
 
261
259
  @fieldset =
@@ -264,7 +262,6 @@
264
262
  else
265
263
  ArrayFields::FieldSet.new fields
266
264
  end
267
- #}}}
268
265
  end
269
266
  #
270
267
  # access to fieldset
@@ -274,19 +271,32 @@
274
271
  # access to field list
275
272
  #
276
273
  def fields
277
- #{{{
278
274
  @fieldset and @fieldset.fields
279
- #}}}
280
275
  end
281
- #}}}
282
276
  end
283
277
  #
284
- # The Array class is extened with a methods to allow keyword access
278
+ # Array instances are extened with two methods only: Fieldable#fields= and
279
+ # Fieldable#fields. only when Fieldable#fields= is called will the full set
280
+ # of ArrayFields methods auto-extend the Array instance. the Array class also
281
+ # has added a class generator when the fields are known apriori.
285
282
  #
286
283
  class Array
287
- #{{{
288
284
  include Fieldable
289
- #}}}
285
+
286
+ class << self
287
+ def fields *fields
288
+ Class.new(self) do
289
+ const_set :FIELDS, ArrayFields::FieldSet.new(fields.flatten)
290
+ include ArrayFields
291
+ def initialize *a, &b
292
+ super
293
+ ensure
294
+ @fieldset = self.class.const_get :FIELDS
295
+ end
296
+ end
297
+ end
298
+ alias_method 'struct', 'fields'
299
+ end
290
300
  end
291
301
  #
292
302
  # proxy class that allows an array to be wrapped in a way that still allows #
@@ -300,48 +310,59 @@
300
310
  #
301
311
  #
302
312
  class FieldedArray
303
- #{{{
304
313
  include Fieldable
305
314
  class << self
306
-
307
315
  def [](*pairs)
308
- #{{{
309
316
  pairs.flatten!
310
- raise ArgumentError, "argument must be key/val paris" unless
317
+ raise ArgumentError, "argument must be key/val pairs" unless
311
318
  (pairs.size % 2 == 0 and pairs.size >= 2)
312
319
  fields, elements = [], []
313
- #pairs.each do |f,e|
314
320
  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
321
  fields << f and elements << e
318
322
  end
319
323
  new fields, elements
320
- #}}}
321
324
  end
322
-
323
325
  end
324
- def initialize fields, array
325
- #{{{
326
+ def initialize fields = [], array = []
326
327
  @a = array
327
328
  self.fields = fields
328
- #}}}
329
329
  end
330
330
  def method_missing(meth, *args, &block)
331
- #{{{
332
331
  @a.send(meth, *args, &block)
333
- #}}}
334
332
  end
335
333
  delegates =
336
- #{{{
337
334
  %w(
338
335
  to_s
339
336
  to_str
340
337
  inspect
341
338
  )
342
- #}}}
343
339
  delegates.each do |meth|
344
340
  class_eval "def #{ meth }(*a,&b); @a.#{ meth }(*a,&b);end"
345
341
  end
346
- #}}}
342
+ end
343
+
344
+ class PseudoHash < ::Array
345
+ class << self
346
+ def [](*pairs)
347
+ pairs.flatten!
348
+ raise ArgumentError, "argument must be key/val pairs" unless
349
+ (pairs.size % 2 == 0 and pairs.size >= 2)
350
+ keys, values = [], []
351
+ while((k = pairs.shift) and (v = pairs.shift))
352
+ keys << k and values << v
353
+ end
354
+ new keys, values
355
+ end
356
+ end
357
+ def initialize keys = [], values = []
358
+ self.replace values
359
+ self.fields = keys
360
+ end
361
+ def to_yaml opts = {}
362
+ YAML::quick_emit object_id, opts do |out|
363
+ out.map taguri, to_yaml_style do |map|
364
+ each_pair{|f,v| map.add f,v}
365
+ end
366
+ end
367
+ end
347
368
  end