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 +173 -64
- data/README.tmpl +147 -0
- data/gemspec.rb +4 -0
- data/gen_readme.rb +32 -0
- data/lib/{arrayfields-3.6.0.rb → arrayfields-3.7.0.rb} +120 -99
- data/lib/arrayfields.rb +120 -99
- data/sample/a.rb +1 -26
- data/sample/b.rb +16 -0
- data/test/arrayfields.rb +55 -55
- metadata +31 -23
- data/VERSION +0 -1
data/README
CHANGED
@@ -1,72 +1,181 @@
|
|
1
|
-
|
1
|
+
NAME
|
2
|
+
arrayfields.rb
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
data/README.tmpl
ADDED
@@ -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 }/"
|
data/gen_readme.rb
ADDED
@@ -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
|
4
|
-
#
|
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
|
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
|
-
|
19
|
-
|
20
|
-
|
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 []
|
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
|
-
|
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
|
-
|
133
|
-
|
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
|
-
|
179
|
-
|
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
|
-
|
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
|
-
|
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
|
-
#
|
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
|
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
|