arrayfields 3.6.0 → 3.7.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.
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