valid_array 0.1.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.
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: