Top Level Namespace
Defined Under Namespace
Classes: Module
Instance Method Summary collapse
-
#Array(type = nil, &block) ⇒ Object
call-seq: Array(class).new -> array class MyClass < Array(class); end class MyClass < Array class do |elem| elem.isvalid end class MyClass < Array {|elem| elem.respond_to?(:meth) }.
Instance Method Details
#Array(type = nil, &block) ⇒ Object
call-seq:
Array(class).new -> array
class MyClass < Array(class); end
class MyClass < Array class do |elem| elem.isvalid end
class MyClass < Array {|elem| elem.respond_to?(:meth) }
Checked array classes are subclasses of #Array that are created using this Array()
method. They provide two kinds of checking on values entered into the array:
-
class checking (using kind_of?)
-
calling a block to determine validity
When any value is entered into the Array, by any method, that doesn’t satisfy your checks, you’ll get a nice exception. Beware of catching this exception, as in some cases (flatten
for instance) the operation has been completed before the new array value is checked and the exception raised.
Here’s a simple example:
int_array = Array(Integer).new
int_array << 4
int_array.concat [ 6, 8, 10 ]
int_array << "Oops" # This will raise an exception
Surprisingly, a call to Array()
works even as a superclass in a new class definition:
class MyArray < Array(Integer)
def my_meth()
...
end
end
and you can even use both together. An exception is raised if the block returns false/nil:
class MyArray < Array(Integer) {|i|
(0..5).include?(i) # Value must be an integer from 0 to 5
}
end
MyArray.new << 6 # This will raise an exception
The parameter to Array is optional, which removes the class check. You can use this to implement type checking that’s all your own:
class MyArray < Array {|i|
i.respond_to?(:+@) && i.respond_to(:to_i)
}
def my_meth()
end
end
Note that these latter examples create two new classes, one from the call to the Array() method, and one that you declared. So you don’t need to worry about overriding the methods that perform the checking; super
works as normal.
There is no way to specify an initial size or a default value.
The methods that are overridden in order to implement the checking are listed below. All documented types of parameter lists and blocks are supported.
-
new
-
Array.[] (constructor)
-
[]=
-
<<
-
concat
-
fill
-
flatten!
-
replace
-
insert
-
collect!
-
map!
-
push
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
# File 'lib/chattr.rb', line 81 def Array(type = nil, &block) Class.new(Array).class_eval <<-END if (Class === type) @@_valid_type = lambda{|o| o.kind_of?(type) && (!block || block.call(o)) } else @@_valid_type ||= (block || lambda{|o| true}) end def self.new(*a, &b) r = super() # Is this correct? if (a.size == 1 && a[0].class != Fixnum) r.concat(a[0]) elsif (b) raise "Wrong number of parameters for Array.new" if a.size != 1 (0...a[0]).each{|i| v = b.call(i) raise "Illegal array member from block initializer: \#{v.inspect}" unless @@_valid_type.call(v) r[i] = v } else v = a[1] || nil if (a[1]) raise "Illegal array member initializer: \#{v.inspect}" unless @@_valid_type.call(v) end if (a.size > 0) (0...a[0]).each_index{|i| r[i] = v } end end r end def self.[](*a) r = self.new r.concat(a) end def []=(*args) element = args.last raise "Illegal array member assignment: \#{element.inspect}" unless @@_valid_type.call(element) super(*args) end def <<(element) raise "Illegal array member append: \#{element.inspect}" unless @@_valid_type.call(element) super(element) end def concat(other) other.each{|e| raise "Illegal array member in concat: \#{e.inspect}" unless @@_valid_type.call(e) } super(other) end def fill(*a, &b) unless b v = a.shift raise "Illegal array value fill: \#{v.inspect}" unless @@_valid_type.call(v) b = lambda{|i| v} end case a.size when 0 # Fill all members: self.each_index{|i| e = b.call(i) self[i] = e } when 1 # Fill start..end or using Range: r = a[0] r = (a[0]..self.size-1) unless r.kind_of?(Range) r.each{|i| e = b.call(i) raise "Illegal array block fill: \#{e.inspect}" unless @@_valid_type.call(e) self[i] = e } when 2 start = a[0] a[0] = Range.new(start, start+a.pop) end self end def check_valid(operation) each{|e| raise "Illegal array element: \#{e.inspect} after \#{operation}" unless @@_valid_type.call(e) } end def flatten!() saved = clone a = super begin check_valid "flatten!" rescue clear concat saved raise end a end def replace(a) saved = clone begin clear concat(a) rescue clear # Restore the value concat saved raise end self end def insert(*a) start = a.shift a.each{|e| raise "Illegal array element insert: \#{e.inspect}" unless @@_valid_type.call(e) } super(start, *a) end def collect! each_with_index{|e, i| v = yield(e) raise "Illegal array element in collect!: \#{v.inspect}" unless @@_valid_type.call(v) self[i] = v } self end def map!(&b) collect!(&b) end def push(*a) concat a self end self END end |