valid_array 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7f027291bafabc6977571d06cae40b0a19a00ed6
4
+ data.tar.gz: 8c62dab2888b7419ecc7466243ad6b08bfdc67d7
5
+ SHA512:
6
+ metadata.gz: 76bfde39f3b266ad440d05b035f2d91d93d63cd8e0c527f467d33d3d3614fde44f60f4194507d45bd06273a4d5bf51821c14eeb8ed6a0f553618887ea3bbd7dd
7
+ data.tar.gz: 4b9c64dbd30920a626d8844b92d8b5ec0af9ac9ebcd3ed75374074599d411efc8a3fe98ce6694a16dd4c89b901bf2e1511428431f96c2862c96bc4b21210b737
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2011 Ryan Biesemeyer
2
+ Copyright (c) 2013 Kevin Cox
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,91 @@
1
+ = valid_array
2
+
3
+ The valid_array gem provides to create an Array that enforces certian
4
+ properties. Each element added to the array is passed to a validator function
5
+ written by you. This function can raise errors, drop items or change them.
6
+
7
+ vaild_array also provides compatibility with the typed-array gem. There is both
8
+ 'valid_array/typed_array' which imports to `ValidArray::TypedArray` and a fully
9
+ compatible 'valid-array' which passes 'valid-array's own test suite.
10
+
11
+ Copyright (c) 2011 Ryan Biesemeyer <mailto:ruby-dev@yaauie.com> (Original typed-array work)
12
+ Copyright (c) 2013 Kevin Cox <mailto:kevincox@kevincox.ca>
13
+ See LICENSE for details.
14
+
15
+ == Examples
16
+
17
+ === ValidArray
18
+
19
+ Valid Array provides a hook to change and validate every element added to the
20
+ array.
21
+
22
+ require 'valid_array'
23
+
24
+ # An Array that converts all added elements into strings.
25
+ class StringArray < Array
26
+ extend ValidArray
27
+
28
+ def self.validate(element)
29
+ element.to_s
30
+ end
31
+ end
32
+
33
+ # An array that enforces Numeric types and silently drops odd numbers.
34
+ class EvenArray < Array
35
+ extend ValidArray
36
+
37
+ class NotEvenException < Exception
38
+ end
39
+
40
+ def self.validate(element)
41
+ if not element.is_a? Numeric
42
+ raise TypeError, "Got #{element.class}, expected Numeric!"
43
+ end
44
+
45
+ if element % 2 == 1
46
+ # This exception is handled by ValidArray, it causes the element to be
47
+ # dropped. All other exceptions are propagated to the caller.
48
+ raise ValidArray::DontInsertException
49
+ end
50
+ end
51
+ end
52
+
53
+ === TypedArray
54
+
55
+ TypedArray is fully compatible with https://github.com/yaauie/typed-array/.
56
+ For compatibility mode require 'typed-array', otherwise require
57
+ 'valid_array/typed_array' and access it from `ValidArray::TypedArray`.
58
+
59
+ === Create Standard Class
60
+
61
+ require 'typed-array'
62
+ class Things < Array
63
+ extend TypedArray
64
+ restrict_types Thing1,Thing2
65
+ end
66
+
67
+ # or new style
68
+
69
+ require 'valid_array/typed_array'
70
+ class Things < Array
71
+ extend ValidArray::TypedArray
72
+ restrict_types Thing1,Thing2
73
+ end
74
+
75
+ === Generate Class using Factory
76
+
77
+ require 'typed-array'
78
+ things = TypedArray(Thing1,Thing2)
79
+ a = things.new
80
+
81
+ # or new style
82
+
83
+ require 'valid_array/typed_array'
84
+ things = ValidArray::TypedArray(Thing1, Thing2)
85
+ a = things.new
86
+
87
+ = Adding items to the Array
88
+
89
+ All standard Array interfaces are implemented, including block-processing
90
+ and variable-number of arguments. For methods that would usually return an
91
+ Array, they instead return an instance of the current class (except to_a).
@@ -0,0 +1,14 @@
1
+ # Provides compatibility with https://github.com/yaauie/typed-array
2
+
3
+ require 'valid_array/typed_array'
4
+
5
+ # Alias for compatibility.
6
+ # see {ValidArray::TypedArray}
7
+ TypedArray = ValidArray::TypedArray
8
+
9
+
10
+ # Alias for compatibility.
11
+ # see {ValidArray::TypedArray}
12
+ def TypedArray(*args)
13
+ ValidArray::TypedArray *args
14
+ end
@@ -0,0 +1,111 @@
1
+ # Provides the validation functions that get included into a ValidArray
2
+
3
+ # Namespace ValidArray
4
+ module ValidArray
5
+
6
+ # The functions that get included into ValidArray
7
+ module Functions
8
+ # Validates outcome. See Array#initialize
9
+ def initialize(*args, &block)
10
+ ary = Array.new *args, &block
11
+ self.replace ary
12
+ end
13
+
14
+ # Validates outcome. See Array#replace
15
+ def replace(other_ary)
16
+ other_ary = _ensure_array_is_valid other_ary
17
+ super
18
+ end
19
+
20
+ # Validates outcome. See Array#&
21
+ def &(ary)
22
+ self.class.new super
23
+ end
24
+
25
+ # Validates outcome. See Array#*
26
+ def *(int)
27
+ self.class.new super
28
+ end
29
+
30
+ # Validates outcome. See Array#+
31
+ def +(ary)
32
+ self.class.new super
33
+ end
34
+
35
+ # Validates outcome. See Array#<<
36
+ def <<(item)
37
+ begin
38
+ item = self.class.validate item
39
+ super
40
+ rescue DontInsertException; end
41
+ end
42
+
43
+ # Validates outcome. See Array#[]
44
+ def [](idx)
45
+ self.class.new super
46
+ end
47
+
48
+ # Validates outcome. See Array#slice
49
+ def slice(*args)
50
+ self.class.new super
51
+ end
52
+
53
+ # Validates outcome. See Array#[]=
54
+ def []=(idx, item)
55
+ begin
56
+ item = self.class.validate item
57
+ super
58
+ rescue DontInsertException; end
59
+ end
60
+
61
+ # Validates outcome. See Array#concat
62
+ def concat(other_ary)
63
+ _ensure_array_is_valid other_ary
64
+ super
65
+ end
66
+
67
+ # Validates outcome. See Array#eql?
68
+ #def eql?(other_ary)
69
+ # _ensure_all_items_in_array_are_allowed other_ary
70
+ # super
71
+ #end
72
+
73
+ # Validates outcome. See Array#fill
74
+ def fill(*args, &block)
75
+ ary = self.to_a
76
+ ary.fill *args, &block
77
+ self.replace ary
78
+ end
79
+
80
+ # Validates outcome. See Array#push
81
+ def push(*items)
82
+ items = _ensure_array_is_valid items
83
+ super
84
+ end
85
+
86
+ # Validates outcome. See Array#unshift
87
+ def unshift(*items)
88
+ items = _ensure_array_is_valid items
89
+ super
90
+ end
91
+
92
+ # Validates outcome. See Array#map!
93
+ def map!(&block)
94
+ self.replace(self.map &block)
95
+ end
96
+
97
+ protected
98
+
99
+ # Ensure that all items in the passed Array are allowed
100
+ def _ensure_array_is_valid(ary)
101
+ toins = []
102
+ ary.each do |e|
103
+ begin
104
+ toins << self.class.validate(e)
105
+ rescue DontInsertException; end
106
+ end
107
+
108
+ toins
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,74 @@
1
+ # :include: ../README.rdoc
2
+
3
+ require 'set'
4
+ require 'valid_array'
5
+
6
+ # Provides ValidArray functionality to a subclass of Array
7
+ # when extended in the class's definiton
8
+ module ValidArray::TypedArray
9
+ # when a class inherits from this one, make sure that it also inherits
10
+ # the types that are being enforced
11
+ def inherited(subclass)
12
+ self._subclasses << subclass
13
+ subclass.restricted_types *self.restricted_types
14
+ end
15
+
16
+ # A getter/setter for types to add. If no arguments are passed, it simply
17
+ # returns the current array of accepted types.
18
+ def restricted_types(*types)
19
+ @_restricted_types ||= []
20
+ types.each do |type|
21
+ raise UnexpectedTypeException.new([Class],type.class) unless type.is_a? Class
22
+ @_restricted_types << type unless @_restricted_types.include? type
23
+ _subclasses.each do |subclass|
24
+ subclass.restricted_types type
25
+ end
26
+ end
27
+ @_restricted_types
28
+ end; alias :restricted_type :restricted_types
29
+
30
+
31
+ # The exception that is raised when an Unexpected Type is reached during validation
32
+ class UnexpectedTypeException < Exception
33
+ # Provide access to the types of objects expected and the class of the object received
34
+ attr_reader :expected, :received
35
+
36
+ def initialize(expected_one_of, received)
37
+ @expected = expected_one_of
38
+ @received = received
39
+ end
40
+
41
+ def to_s
42
+ %{Expected one of #{@expected.inspect} but received a(n) #{@received}}
43
+ end
44
+ end
45
+
46
+ def validate(item)
47
+ if item.nil? or restricted_types.any? { |allowed| item.class <= allowed }
48
+ return item
49
+ else
50
+ raise TypedArray::UnexpectedTypeException.new(restricted_types, item.class)
51
+ end
52
+ end
53
+
54
+ protected
55
+
56
+ # a store of subclasses
57
+ def _subclasses
58
+ @_subclasses ||= []
59
+ end
60
+
61
+ end
62
+
63
+ # Provide a factory method. Takes any number of types to accept as arguments
64
+ # and returns a class that behaves as a type-enforced array.
65
+ def ValidArray::TypedArray(*types_allowed)
66
+ klass = Class.new( Array )
67
+ klass.class_exec(types_allowed) do |types_allowed|
68
+ extend ValidArray
69
+ extend TypedArray
70
+
71
+ restricted_types *types_allowed
72
+ end
73
+ klass
74
+ end
@@ -0,0 +1,34 @@
1
+ # :include: ../README.rdoc
2
+
3
+ require 'valid_array/functions'
4
+
5
+ # Provides ValidArray functionality to a subclass of Array
6
+ # when extended in the class's definiton
7
+ module ValidArray
8
+ # Hook the extension process in order to include the necessary functions
9
+ # and do some basic sanity checks.
10
+ def self.extended(mod)
11
+ mod.module_exec(self::Functions) do |functions_module|
12
+ include functions_module
13
+ end
14
+ end
15
+
16
+ # Exception to raise to silently skip insertion.
17
+ #
18
+ # Raising this from validate will prevent the item from being inserted. Use
19
+ # with caution as it may break assumptions that coders are making about how
20
+ # your array works.
21
+ class DontInsertException < Exception; end
22
+
23
+ # Default validator. Override this.
24
+ #
25
+ # Translates the provided item to insert to the new item. Raise an exception
26
+ # to prevent insertion. DontInsertException will be handled and ignored,
27
+ # other exceptions will be propergated.
28
+ #
29
+ # @param element [Object] The element being added to the array.
30
+ # @return [Object] The element to actualy insert into the array.
31
+ def validate(element)
32
+ raise NotImplementedError, "You must implement validate."
33
+ end
34
+ end
@@ -0,0 +1,6 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'rspec'
3
+
4
+ RSpec.configure do |config|
5
+
6
+ end
@@ -0,0 +1,278 @@
1
+ require_relative 'spec_helper'
2
+ require 'typed-array'
3
+
4
+ # We need to test in several different contexts, so provide the base info here.
5
+ inputs = {
6
+ [Symbol]=>{
7
+ :base => [:foo,:bar],
8
+ :match => [:baz,:bingo],
9
+ :fail => {
10
+ :one => [:zip,0],
11
+ :all => ['string',Class]
12
+ }
13
+ },
14
+ [Fixnum] => {
15
+ :base => [ 1,2,3 ],
16
+ :match => [ 17,18,19 ],
17
+ :fail => {
18
+ :one => [22,:symbol,43],
19
+ :all => ['string',:symbol]
20
+ }
21
+ },
22
+ [String,Symbol] => {
23
+ :base => ['Foo',:foo],
24
+ :match => [:bar,'Bar'],
25
+ :fail => {
26
+ :one => [:yippee,'a string',17],
27
+ :all => [12,Class,Array]
28
+ }
29
+ }
30
+ }
31
+
32
+ describe TypedArray do
33
+ describe '#new' do
34
+ inputs.each_pair do |accepted_types, config|
35
+ context "when only accepting <#{accepted_types.inspect}>" do
36
+ subject { TypedArray( *accepted_types ) }
37
+
38
+ context 'Form 1: typed_ary.new()' do
39
+ it "should have zero-length" do
40
+ subject.new().length.should == 0
41
+ end
42
+
43
+ it "should be empty" do
44
+ subject.new().to_a.should be_empty
45
+ end
46
+ end
47
+
48
+ context 'Form 1: typed_ary.new(size)' do
49
+ it "should have the proper length" do
50
+ subject.new(5).length.should == 5
51
+ end
52
+
53
+ it "should conatin all nil values" do
54
+ subject.new(5).to_a.should == [nil,nil,nil,nil,nil]
55
+ end
56
+ end
57
+
58
+ context 'Form 2: typed_ary.new(size,object)' do
59
+ it "should have the proper length" do
60
+ subject.new(3,config[:match].first).length.should == 3
61
+ end
62
+
63
+ it "should conatin the value specified" do
64
+ subject.new(3,config[:match].first).to_a.should == [config[:match].first]*3
65
+ end
66
+
67
+ it "should raise when obj is the wrong type" do
68
+ expect{ subject.new( 3, config[:fail][:all].first ) }.to raise_error TypedArray::UnexpectedTypeException
69
+ end
70
+ end
71
+
72
+ context 'Form 3: typed_ary.new( ary )' do
73
+ it "should accept when all items match" do
74
+ subject.new(config[:match]).to_a.should == config[:match]
75
+ end
76
+
77
+ it "should raise when one object is the wrong type" do
78
+ expect{ subject.new(config[:fail][:one])}.to raise_error TypedArray::UnexpectedTypeException
79
+ end
80
+
81
+ it "should raise when more than one object is the wrong type" do
82
+ expect{ subject.new(config[:fail][:all])}.to raise_error TypedArray::UnexpectedTypeException
83
+ end
84
+ end
85
+
86
+ context 'Form 4: typed_ary.new(size){|index|block}' do
87
+ it "should populate when block returns the right type" do
88
+ subject.new(config[:match].length){|i| config[:match][i]}.to_a.should == config[:match]
89
+ end
90
+
91
+ it "should raise when block returns wrong type once" do
92
+ expect{ subject.new(config[:fail][:one].length){|i| config[:fail][:one][i]} }.to raise_error TypedArray::UnexpectedTypeException
93
+ end
94
+
95
+ it "should raise when block returns wrong type more than once" do
96
+ expect{ subject.new(config[:fail][:all].length){|i| config[:fail][:all][i]} }.to raise_error TypedArray::UnexpectedTypeException
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ [:<<,:unshift,:push].each do |method|
104
+ context %Q{typed_ary#{('a'..'z').include?(method.to_s[0]) ? '.' : ' '}#{method.to_s} other_ary} do
105
+ inputs.each_pair do |accepted_types,config|
106
+ context "when only accepting <#{accepted_types.inspect}>" do
107
+ before :each do
108
+ @typed_ary = TypedArray( *accepted_types).new(config[:base])
109
+ @ary = config[:base].to_a
110
+ end
111
+
112
+ context "when the item being pushed matches (#{config[:match].first})" do
113
+ before :each do
114
+ @item = config[:match].first
115
+ end
116
+
117
+ it "should return as Array would return" do
118
+ @typed_ary.send(method,@item).to_a.should == @ary.send(method,@item)
119
+ end
120
+
121
+ it "should modify the TypedArray as Array would be modified" do
122
+ @typed_ary.send(method,@item)
123
+ @ary.send(method,@item)
124
+ @typed_ary.to_a.should == @ary
125
+ end
126
+ end
127
+
128
+ context "when the item being pushed does not match (#{config[:fail][:all].first})" do
129
+ before :each do
130
+ @item = config[:fail][:all].first
131
+ end
132
+
133
+ it "should raise an exception" do
134
+ expect{ @typed_ary.send(method,@item) }.to raise_error TypedArray::UnexpectedTypeException
135
+ end
136
+
137
+ it "should not modify typed_ary" do
138
+ begin
139
+ backup = @typed_ary.to_a
140
+ @typed_ary.send(method,@item)
141
+ rescue TypedArray::UnexpectedTypeException
142
+ ensure
143
+ @typed_ary.to_a.should == backup
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ [:[]=].each do |method|
153
+ context %Q{typed_ary[idx]= other_ary} do
154
+ inputs.each_pair do |accepted_types,config|
155
+ context "when only accepting <#{accepted_types.inspect}>" do
156
+ before :each do
157
+ @typed_ary = TypedArray( *accepted_types).new(config[:base])
158
+ @ary = config[:base].to_a
159
+ end
160
+
161
+ context "when the item being pushed matches (#{config[:match].first})" do
162
+ before :each do
163
+ @item = config[:match].first
164
+ end
165
+
166
+ it "should return as Array would return" do
167
+ @typed_ary.send(method,4,@item).should == @ary.send(method,4,@item)
168
+ end
169
+
170
+ it "should modify the TypedArray as Array would be modified" do
171
+ @typed_ary.send(method,4,@item)
172
+ @ary.send(method,4,@item)
173
+ @typed_ary.to_a.should == @ary
174
+ end
175
+ end
176
+
177
+ context "when the item being pushed does not match (#{config[:fail][:all].first})" do
178
+ before :each do
179
+ @item = config[:fail][:all].first
180
+ end
181
+
182
+ it "should raise an exception" do
183
+
184
+ expect{ @typed_ary.send(method,4,@item) }.to raise_error TypedArray::UnexpectedTypeException
185
+ end
186
+
187
+ it "should not modify typed_ary" do
188
+ begin
189
+ backup = @typed_ary.to_a
190
+ @typed_ary.send(method,4,@item)
191
+ rescue TypedArray::UnexpectedTypeException
192
+ ensure
193
+ @typed_ary.to_a.should == backup
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ [:+,:&,:concat,:replace].each do |method|
203
+ context %Q{typed_ary#{('a'..'z').include?(method.to_s[0]) ? '.' : ' '}#{method.to_s} other_ary} do
204
+ inputs.each_pair do |accepted_types,config|
205
+ context "when only accepting <#{accepted_types.inspect}>" do
206
+ before :each do
207
+ @typed_ary = TypedArray( *accepted_types).new(config[:base])
208
+ @ary = config[:base].to_a
209
+ end
210
+
211
+ context "when all items match (#{config[:match].inspect})" do
212
+ before :each do
213
+ @other_ary = config[:match].to_a
214
+ end
215
+
216
+ it "should return as Array would return" do
217
+ @typed_ary.send(method,@other_ary).to_a.should == @ary.send(method,@other_ary)
218
+ end
219
+
220
+ it "should modify the TypedArray as Array would be modified" do
221
+ @typed_ary.send(method,@other_ary)
222
+ @ary.send(method,@other_ary)
223
+ @typed_ary.to_a.should == @ary
224
+ end
225
+ end
226
+
227
+ config[:fail].each_key do |fail_type|
228
+ context "when #{fail_type} item fails to match (#{config[:fail][fail_type].inspect})" do
229
+ before :each do
230
+ @other_ary = config[:fail][fail_type].to_a
231
+ end
232
+ unless method == :& # `and` opperator cannot produce elements that are not in both arrays already; since one is assuredly filtered, we can skip this.
233
+ it "should raise an exception" do
234
+ expect{ @typed_ary.send(method,@other_ary) }.to raise_error TypedArray::UnexpectedTypeException
235
+ end
236
+ end
237
+
238
+ it "should not modify the TypedArray" do
239
+ begin
240
+ backup = @typed_ary.to_a
241
+ @typed_ary.send(method,@other_ary)
242
+ rescue TypedArray::UnexpectedTypeException
243
+ ensure
244
+ @typed_ary.to_a.should == backup
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ context 'when extending classes' do
255
+ before :each do
256
+ @base = TypedArray(Symbol)
257
+ @extension = Class.new( @base )
258
+ end
259
+
260
+ it 'should inherit default restrictions' do
261
+ @base.restricted_types.should == @extension.restricted_types
262
+ end
263
+
264
+ context 'when adding restricted_type to the parent' do
265
+ it 'should propogate to the child' do
266
+ @base.restricted_type Fixnum
267
+ @extension.restricted_types.should include(Fixnum)
268
+ end
269
+ end
270
+
271
+ context 'when adding restricted_type to the child' do
272
+ it 'should not propogate to the parent' do
273
+ @extension.restricted_type Fixnum
274
+ @base.restricted_types.should_not include(Fixnum)
275
+ end
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,278 @@
1
+ require_relative 'spec_helper'
2
+ require 'valid_array/typed_array'
3
+
4
+ # We need to test in several different contexts, so provide the base info here.
5
+ inputs = {
6
+ [Symbol]=>{
7
+ :base => [:foo,:bar],
8
+ :match => [:baz,:bingo],
9
+ :fail => {
10
+ :one => [:zip,0],
11
+ :all => ['string',Class]
12
+ }
13
+ },
14
+ [Fixnum] => {
15
+ :base => [ 1,2,3 ],
16
+ :match => [ 17,18,19 ],
17
+ :fail => {
18
+ :one => [22,:symbol,43],
19
+ :all => ['string',:symbol]
20
+ }
21
+ },
22
+ [String,Symbol] => {
23
+ :base => ['Foo',:foo],
24
+ :match => [:bar,'Bar'],
25
+ :fail => {
26
+ :one => [:yippee,'a string',17],
27
+ :all => [12,Class,Array]
28
+ }
29
+ }
30
+ }
31
+
32
+ describe ValidArray::TypedArray do
33
+ describe '#new' do
34
+ inputs.each_pair do |accepted_types, config|
35
+ context "when only accepting <#{accepted_types.inspect}>" do
36
+ subject { ValidArray::TypedArray( *accepted_types ) }
37
+
38
+ context 'Form 1: typed_ary.new()' do
39
+ it "should have zero-length" do
40
+ subject.new().length.should == 0
41
+ end
42
+
43
+ it "should be empty" do
44
+ subject.new().to_a.should be_empty
45
+ end
46
+ end
47
+
48
+ context 'Form 1: typed_ary.new(size)' do
49
+ it "should have the proper length" do
50
+ subject.new(5).length.should == 5
51
+ end
52
+
53
+ it "should conatin all nil values" do
54
+ subject.new(5).to_a.should == [nil,nil,nil,nil,nil]
55
+ end
56
+ end
57
+
58
+ context 'Form 2: typed_ary.new(size,object)' do
59
+ it "should have the proper length" do
60
+ subject.new(3,config[:match].first).length.should == 3
61
+ end
62
+
63
+ it "should conatin the value specified" do
64
+ subject.new(3,config[:match].first).to_a.should == [config[:match].first]*3
65
+ end
66
+
67
+ it "should raise when obj is the wrong type" do
68
+ expect{ subject.new( 3, config[:fail][:all].first ) }.to raise_error ValidArray::TypedArray::UnexpectedTypeException
69
+ end
70
+ end
71
+
72
+ context 'Form 3: typed_ary.new( ary )' do
73
+ it "should accept when all items match" do
74
+ subject.new(config[:match]).to_a.should == config[:match]
75
+ end
76
+
77
+ it "should raise when one object is the wrong type" do
78
+ expect{ subject.new(config[:fail][:one])}.to raise_error ValidArray::TypedArray::UnexpectedTypeException
79
+ end
80
+
81
+ it "should raise when more than one object is the wrong type" do
82
+ expect{ subject.new(config[:fail][:all])}.to raise_error ValidArray::TypedArray::UnexpectedTypeException
83
+ end
84
+ end
85
+
86
+ context 'Form 4: typed_ary.new(size){|index|block}' do
87
+ it "should populate when block returns the right type" do
88
+ subject.new(config[:match].length){|i| config[:match][i]}.to_a.should == config[:match]
89
+ end
90
+
91
+ it "should raise when block returns wrong type once" do
92
+ expect{ subject.new(config[:fail][:one].length){|i| config[:fail][:one][i]} }.to raise_error ValidArray::TypedArray::UnexpectedTypeException
93
+ end
94
+
95
+ it "should raise when block returns wrong type more than once" do
96
+ expect{ subject.new(config[:fail][:all].length){|i| config[:fail][:all][i]} }.to raise_error ValidArray::TypedArray::UnexpectedTypeException
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ [:<<,:unshift,:push].each do |method|
104
+ context %Q{typed_ary#{('a'..'z').include?(method.to_s[0]) ? '.' : ' '}#{method.to_s} other_ary} do
105
+ inputs.each_pair do |accepted_types,config|
106
+ context "when only accepting <#{accepted_types.inspect}>" do
107
+ before :each do
108
+ @typed_ary = ValidArray::TypedArray( *accepted_types).new(config[:base])
109
+ @ary = config[:base].to_a
110
+ end
111
+
112
+ context "when the item being pushed matches (#{config[:match].first})" do
113
+ before :each do
114
+ @item = config[:match].first
115
+ end
116
+
117
+ it "should return as Array would return" do
118
+ @typed_ary.send(method,@item).to_a.should == @ary.send(method,@item)
119
+ end
120
+
121
+ it "should modify the TypedArray as Array would be modified" do
122
+ @typed_ary.send(method,@item)
123
+ @ary.send(method,@item)
124
+ @typed_ary.to_a.should == @ary
125
+ end
126
+ end
127
+
128
+ context "when the item being pushed does not match (#{config[:fail][:all].first})" do
129
+ before :each do
130
+ @item = config[:fail][:all].first
131
+ end
132
+
133
+ it "should raise an exception" do
134
+ expect{ @typed_ary.send(method,@item) }.to raise_error ValidArray::TypedArray::UnexpectedTypeException
135
+ end
136
+
137
+ it "should not modify typed_ary" do
138
+ begin
139
+ backup = @typed_ary.to_a
140
+ @typed_ary.send(method,@item)
141
+ rescue ValidArray::TypedArray::UnexpectedTypeException
142
+ ensure
143
+ @typed_ary.to_a.should == backup
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+
152
+ [:[]=].each do |method|
153
+ context %Q{typed_ary[idx]= other_ary} do
154
+ inputs.each_pair do |accepted_types,config|
155
+ context "when only accepting <#{accepted_types.inspect}>" do
156
+ before :each do
157
+ @typed_ary = ValidArray::TypedArray( *accepted_types).new(config[:base])
158
+ @ary = config[:base].to_a
159
+ end
160
+
161
+ context "when the item being pushed matches (#{config[:match].first})" do
162
+ before :each do
163
+ @item = config[:match].first
164
+ end
165
+
166
+ it "should return as Array would return" do
167
+ @typed_ary.send(method,4,@item).should == @ary.send(method,4,@item)
168
+ end
169
+
170
+ it "should modify the TypedArray as Array would be modified" do
171
+ @typed_ary.send(method,4,@item)
172
+ @ary.send(method,4,@item)
173
+ @typed_ary.to_a.should == @ary
174
+ end
175
+ end
176
+
177
+ context "when the item being pushed does not match (#{config[:fail][:all].first})" do
178
+ before :each do
179
+ @item = config[:fail][:all].first
180
+ end
181
+
182
+ it "should raise an exception" do
183
+
184
+ expect{ @typed_ary.send(method,4,@item) }.to raise_error ValidArray::TypedArray::UnexpectedTypeException
185
+ end
186
+
187
+ it "should not modify typed_ary" do
188
+ begin
189
+ backup = @typed_ary.to_a
190
+ @typed_ary.send(method,4,@item)
191
+ rescue ValidArray::TypedArray::UnexpectedTypeException
192
+ ensure
193
+ @typed_ary.to_a.should == backup
194
+ end
195
+ end
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
201
+
202
+ [:+,:&,:concat,:replace].each do |method|
203
+ context %Q{typed_ary#{('a'..'z').include?(method.to_s[0]) ? '.' : ' '}#{method.to_s} other_ary} do
204
+ inputs.each_pair do |accepted_types,config|
205
+ context "when only accepting <#{accepted_types.inspect}>" do
206
+ before :each do
207
+ @typed_ary = ValidArray::TypedArray( *accepted_types).new(config[:base])
208
+ @ary = config[:base].to_a
209
+ end
210
+
211
+ context "when all items match (#{config[:match].inspect})" do
212
+ before :each do
213
+ @other_ary = config[:match].to_a
214
+ end
215
+
216
+ it "should return as Array would return" do
217
+ @typed_ary.send(method,@other_ary).to_a.should == @ary.send(method,@other_ary)
218
+ end
219
+
220
+ it "should modify the TypedArray as Array would be modified" do
221
+ @typed_ary.send(method,@other_ary)
222
+ @ary.send(method,@other_ary)
223
+ @typed_ary.to_a.should == @ary
224
+ end
225
+ end
226
+
227
+ config[:fail].each_key do |fail_type|
228
+ context "when #{fail_type} item fails to match (#{config[:fail][fail_type].inspect})" do
229
+ before :each do
230
+ @other_ary = config[:fail][fail_type].to_a
231
+ end
232
+ unless method == :& # `and` opperator cannot produce elements that are not in both arrays already; since one is assuredly filtered, we can skip this.
233
+ it "should raise an exception" do
234
+ expect{ @typed_ary.send(method,@other_ary) }.to raise_error ValidArray::TypedArray::UnexpectedTypeException
235
+ end
236
+ end
237
+
238
+ it "should not modify the TypedArray" do
239
+ begin
240
+ backup = @typed_ary.to_a
241
+ @typed_ary.send(method,@other_ary)
242
+ rescue ValidArray::TypedArray::UnexpectedTypeException
243
+ ensure
244
+ @typed_ary.to_a.should == backup
245
+ end
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
253
+
254
+ context 'when extending classes' do
255
+ before :each do
256
+ @base = ValidArray::TypedArray(Symbol)
257
+ @extension = Class.new( @base )
258
+ end
259
+
260
+ it 'should inherit default restrictions' do
261
+ @base.restricted_types.should == @extension.restricted_types
262
+ end
263
+
264
+ context 'when adding restricted_type to the parent' do
265
+ it 'should propogate to the child' do
266
+ @base.restricted_type Fixnum
267
+ @extension.restricted_types.should include(Fixnum)
268
+ end
269
+ end
270
+
271
+ context 'when adding restricted_type to the child' do
272
+ it 'should not propogate to the parent' do
273
+ @extension.restricted_type Fixnum
274
+ @base.restricted_types.should_not include(Fixnum)
275
+ end
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,45 @@
1
+ require_relative 'spec_helper'
2
+ require 'valid_array'
3
+
4
+ describe ValidArray do
5
+ context 'when custom validator is used' do
6
+ before :each do
7
+ class MyArray < Array
8
+ extend ValidArray
9
+
10
+ def self.validate(e)
11
+ e.to_s
12
+ end
13
+ end
14
+ @typed_ary = MyArray.new(5) {|i| i}
15
+ end
16
+
17
+ it 'should convert all values to strings' do
18
+ @typed_ary.each do |e|
19
+ e.should be_an_instance_of(String)
20
+ end
21
+ end
22
+ end
23
+
24
+ context 'when custom validator is used' do
25
+ before :each do
26
+ class MyArray < Array
27
+ extend ValidArray
28
+
29
+ def self.validate(e)
30
+ if e%2 == 0
31
+ e
32
+ else
33
+ raise ValidArray::DontInsertException
34
+ end
35
+ end
36
+ end
37
+
38
+ @typed_ary = MyArray.new(10) {|i| i}
39
+ end
40
+
41
+ it 'should drop when DontInsertException is raised' do
42
+ @typed_ary.should eql([0,2,4,6,8])
43
+ end
44
+ end
45
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: valid_array
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Cox
8
+ - Ryan Biesemeyer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rdoc
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '3.9'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '3.9'
28
+ description: "\t\tAll methods that alter the contents of an array that implements
29
+ this\n\t\tGem are first checked to ensure that the added items are of the types\n\t\tallowed.
30
+ All methods behave exactly as their Array counterparts,\n\t\tincluding additional
31
+ forms, block processing, etc.\n"
32
+ email: kevincox@kevincox.ca
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files:
36
+ - LICENSE
37
+ - README.rdoc
38
+ files:
39
+ - lib/valid_array.rb
40
+ - lib/valid_array/typed_array.rb
41
+ - lib/valid_array/functions.rb
42
+ - lib/typed-array.rb
43
+ - spec/spec_helper.rb
44
+ - spec/valid_array_spec.rb
45
+ - spec/typed-array_spec.rb
46
+ - spec/typed_array_spec.rb
47
+ - LICENSE
48
+ - README.rdoc
49
+ homepage: http://github.com/kevincox/valid-array
50
+ licenses:
51
+ - MIT
52
+ metadata: {}
53
+ post_install_message:
54
+ rdoc_options: []
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ required_rubygems_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ requirements: []
68
+ rubyforge_project:
69
+ rubygems_version: 2.0.3
70
+ signing_key:
71
+ specification_version: 1
72
+ summary: Provides methods for creating type-enforced Arrays
73
+ test_files: []
74
+ has_rdoc: