cardshark 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Cardshark
4
+ class Error < StandardError
5
+ class AbstractClass < Error; end
6
+ class DeckAlreadyShuffled < Error; end
7
+ class DeckExhausted < Error; end
8
+ class DeckOrderInvalid < Error; end
9
+ class DimensionInheritanceLimit < Error; end
10
+ end
11
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Cardshark
2
- VERSION = "0.0.1"
4
+ VERSION = '0.0.2'
3
5
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cardshark/abstract'
4
+
5
+ RSpec.describe Cardshark::Abstract do
6
+ context 'included into a new class' do
7
+ let(:klass) { Class.new { include Cardshark::Abstract } }
8
+
9
+ describe '::new' do
10
+ it 'raises a Cardshark::Error::AbstractClass' do
11
+ expect { klass.new }.to raise_error(Cardshark::Error::AbstractClass)
12
+ end
13
+
14
+ context 'it has been subclassed' do
15
+ let(:subclass) { Class.new(klass) }
16
+
17
+ it 'does not raise an error' do
18
+ expect { subclass.new }.not_to raise_error
19
+ end
20
+
21
+ it 'returns an instance of the subclass' do
22
+ expect(subclass.new).to be_a(subclass)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cardshark/card'
4
+
5
+ RSpec.describe Cardshark::Card do
6
+ let(:rank) { Class.new(Cardshark::Dimension) }
7
+ let(:suit) { Class.new(Cardshark::Dimension) }
8
+
9
+ let(:ace) { rank.new(:ace) }
10
+ let(:spades) { suit.new(:spades) }
11
+ let(:clubs) { suit.new(:clubs) }
12
+
13
+ describe '::new' do
14
+ context 'invalid arguments' do
15
+ context 'no arguments' do
16
+ it 'raises an ArgumentError' do
17
+ expect { described_class.new }.to raise_error(ArgumentError)
18
+ end
19
+ end
20
+
21
+ context 'invalid argument type' do
22
+ it 'raises an ArgumentError' do
23
+ expect { described_class.new('invalid-arguments') }
24
+ .to raise_error(ArgumentError)
25
+ end
26
+ end
27
+
28
+ context 'more than one argument of the same dimension' do
29
+ it 'raises an ArgumentError' do
30
+ expect { described_class.new(spades, clubs) }
31
+ .to raise_error(ArgumentError)
32
+ end
33
+ end
34
+ end
35
+
36
+ context 'valid arguments' do
37
+ it 'does not raise an error' do
38
+ expect { described_class.new(ace, spades) }.not_to raise_error
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#dimensions' do
44
+ let(:card) { described_class.new(ace, spades) }
45
+
46
+ before do
47
+ @result = card.dimensions
48
+ end
49
+
50
+ it 'returns an Array' do
51
+ expect(@result).to be_an(Array)
52
+ end
53
+
54
+ it 'contains one entry for each type of dimension' do
55
+ expect(@result.count).to eq(2)
56
+ end
57
+
58
+ it 'contains a symbol for the first dimension' do
59
+ expect(@result).to include(rank.id)
60
+ end
61
+
62
+ it 'contains a symbol for the second dimension' do
63
+ expect(@result).to include(suit.id)
64
+ end
65
+ end
66
+
67
+ describe 'dynamic dimension methods' do
68
+ let(:card) { described_class.new(ace, spades) }
69
+
70
+ it 'has a method for the first dimension' do
71
+ expect(card.respond_to?(suit.id)).to eq(true)
72
+ end
73
+
74
+ it 'has a method for the second dimension' do
75
+ expect(card.respond_to?(rank.id)).to eq(true)
76
+ end
77
+
78
+ it 'returns the correct value for the first dimension' do
79
+ expect(card.send(suit.id)).to eq(spades)
80
+ end
81
+
82
+ it 'returns the correct value for the second dimension' do
83
+ expect(card.send(rank.id)).to eq(ace)
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cardshark/card'
4
+ require 'cardshark/deck'
5
+
6
+ RSpec.describe Cardshark::Deck do
7
+ context 'class methods' do
8
+ describe '::new' do
9
+ context 'invalid arguments' do
10
+ context 'no arguments' do
11
+ it 'raises an ArgumentError' do
12
+ expect { described_class.new }.to raise_error(ArgumentError)
13
+ end
14
+ end
15
+
16
+ context 'invalid arguments' do
17
+ it 'raises an ArgumentError' do
18
+ expect { described_class.new('invalid-arguments') }
19
+ .to raise_error(ArgumentError)
20
+ end
21
+ end
22
+
23
+ context 'valid arguments' do
24
+ it 'does not raise an error' do
25
+ expect { described_class.new {} }.not_to raise_error
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ context 'instance methods' do
33
+ let(:suit) { Class.new(Cardshark::Dimension) }
34
+ let!(:spades) { suit.new(:spades) }
35
+ let!(:hearts) { suit.new(:hearts) }
36
+
37
+ let(:rank) { Class.new(Cardshark::Dimension) }
38
+ let!(:king) { rank.new(:king) }
39
+ let!(:queen) { rank.new(:queen) }
40
+ let!(:jack) { rank.new(:jack) }
41
+
42
+ let(:cards) do
43
+ cards = []
44
+ suit.all.each do |suit|
45
+ rank.all.each do |rank|
46
+ cards << Cardshark::Card.new(rank, suit)
47
+ end
48
+ end
49
+ cards
50
+ end
51
+
52
+ describe '#draw' do
53
+ context 'new deck' do
54
+ let(:deck) { described_class.new { cards } }
55
+
56
+ before do
57
+ @one = deck.draw
58
+ @two = deck.draw
59
+ @three = deck.draw
60
+ @four = deck.draw
61
+ @five = deck.draw
62
+ @six = deck.draw
63
+ end
64
+
65
+ it 'returns a Cardshark::Card' do
66
+ expect(@one).to be_a(Cardshark::Card)
67
+ end
68
+
69
+ it 'returns the first card first' do
70
+ expect(@one).to eq(cards[0])
71
+ end
72
+
73
+ it 'returns the second card second' do
74
+ expect(@two).to eq(cards[1])
75
+ end
76
+
77
+ it 'returns the third card third' do
78
+ expect(@three).to eq(cards[2])
79
+ end
80
+
81
+ it 'returns the fourth card fourth' do
82
+ expect(@four).to eq(cards[3])
83
+ end
84
+
85
+ it 'returns the fifth card fifth' do
86
+ expect(@five).to eq(cards[4])
87
+ end
88
+
89
+ it 'returns the sixth card sixth' do
90
+ expect(@six).to eq(cards[5])
91
+ end
92
+
93
+ context 'all cards drawn' do
94
+ it 'raises an error' do
95
+ expect { deck.draw }.to raise_error(Cardshark::Error::DeckExhausted)
96
+ end
97
+ end
98
+ end
99
+
100
+ context 'shuffled deck' do
101
+ let(:deck) { described_class.new { cards } }
102
+
103
+ before do
104
+ deck.shuffle!([3, 4, 1, 5, 2, 0])
105
+
106
+ @one = deck.draw
107
+ @two = deck.draw
108
+ @three = deck.draw
109
+ @four = deck.draw
110
+ @five = deck.draw
111
+ @six = deck.draw
112
+ end
113
+
114
+ it 'returns a Cardshark::Card' do
115
+ expect(@one).to be_a(Cardshark::Card)
116
+ end
117
+
118
+ it 'returns the fourth card first' do
119
+ expect(@one).to eq(cards[3])
120
+ end
121
+
122
+ it 'returns the fifth card second' do
123
+ expect(@two).to eq(cards[4])
124
+ end
125
+
126
+ it 'returns the second card third' do
127
+ expect(@three).to eq(cards[1])
128
+ end
129
+
130
+ it 'returns the sixth card fourth' do
131
+ expect(@four).to eq(cards[5])
132
+ end
133
+
134
+ it 'returns the third card fifth' do
135
+ expect(@five).to eq(cards[2])
136
+ end
137
+
138
+ it 'returns the first card sixth' do
139
+ expect(@six).to eq(cards[0])
140
+ end
141
+
142
+ context 'all cards drawn' do
143
+ it 'raises an error' do
144
+ expect { deck.draw }.to raise_error(Cardshark::Error::DeckExhausted)
145
+ end
146
+ end
147
+ end
148
+ end
149
+
150
+ describe '#count' do
151
+ let(:deck) { described_class.new { cards } }
152
+
153
+ it 'returns the correct number of cards' do
154
+ expect(deck.count).to eq(6)
155
+ end
156
+ end
157
+
158
+ describe '#shuffled?' do
159
+ context 'new deck' do
160
+ let(:deck) { described_class.new { cards } }
161
+
162
+ it 'returns false' do
163
+ expect(deck.shuffled?).to eq(false)
164
+ end
165
+ end
166
+
167
+ context 'shuffled deck' do
168
+ let(:deck) { described_class.new { cards } }
169
+
170
+ before do
171
+ deck.shuffle!([3, 1, 2, 0, 5, 4])
172
+ end
173
+
174
+ it 'returns true' do
175
+ expect(deck.shuffled?).to eq(true)
176
+ end
177
+ end
178
+ end
179
+
180
+ describe 'shuffle!' do
181
+ let(:deck) { described_class.new { cards } }
182
+
183
+ context 'invalid arguments' do
184
+ context 'no arguments' do
185
+ it 'raises an ArgumentError' do
186
+ expect { deck.shuffle! }.to raise_error(ArgumentError)
187
+ end
188
+ end
189
+
190
+ context 'wrong argument type' do
191
+ it 'raises an ArgumentError' do
192
+ expect { deck.shuffle!('invalid-argument') }
193
+ .to raise_error(ArgumentError)
194
+ end
195
+ end
196
+
197
+ context 'invalid deck mapping' do
198
+ context 'non-integer elements' do
199
+ it 'raises an Cardshark::Error::DeckOrderInvalid' do
200
+ expect { deck.shuffle!(['a', 1, 2, 3, 4, 5]) }
201
+ .to raise_error(Cardshark::Error::DeckOrderInvalid)
202
+ end
203
+ end
204
+
205
+ context 'elements out of bounds' do
206
+ it 'raises an Cardshark::Error::DeckOrderInvalid' do
207
+ expect { deck.shuffle!([0, 1, 2, 3, 4, 6]) }
208
+ .to raise_error(Cardshark::Error::DeckOrderInvalid)
209
+ end
210
+ end
211
+
212
+ context 'too many elements' do
213
+ it 'raises an Cardshark::Error::DeckOrderInvalid' do
214
+ expect { deck.shuffle!([0, 1, 2, 3, 4, 5, 6]) }
215
+ .to raise_error(Cardshark::Error::DeckOrderInvalid)
216
+ end
217
+ end
218
+
219
+ context 'too few elements' do
220
+ it 'raises an Cardshark::Error::DeckOrderInvalid' do
221
+ expect { deck.shuffle!([0, 1, 2, 3]) }
222
+ .to raise_error(Cardshark::Error::DeckOrderInvalid)
223
+ end
224
+ end
225
+ end
226
+ end
227
+
228
+ context 'valid arguments' do
229
+ let(:deck_mapping) { [5, 4, 3, 2, 1, 0] }
230
+
231
+ before do
232
+ @result = deck.shuffle!(deck_mapping)
233
+ end
234
+
235
+ it 'returns an instance of the described class' do
236
+ expect(@result).to be_a(described_class)
237
+ end
238
+
239
+ it 'returns the same instance' do
240
+ expect(@result).to equal(deck)
241
+ end
242
+
243
+ context 'alredy shuffled' do
244
+ it 'raises a Cardshark::Error::AlreadyShuffled' do
245
+ expect { deck.shuffle!(deck_mapping) }
246
+ .to raise_error(Cardshark::Error::DeckAlreadyShuffled)
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,113 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cardshark/dimension'
4
+
5
+ RSpec.describe Cardshark::Dimension do
6
+ describe '::new' do
7
+ it 'raises an Error::AbstractClass' do
8
+ expect { described_class.new(:id) }
9
+ .to raise_exception(Cardshark::Error::AbstractClass)
10
+ end
11
+ end
12
+
13
+ context 'when subclassed' do
14
+ let(:subclass) { Class.new(described_class) }
15
+
16
+ describe '::new' do
17
+ context 'invalid arguments' do
18
+ context 'no arguments' do
19
+ it 'raises an ArgumentError' do
20
+ expect { subclass.new }.to raise_exception(ArgumentError)
21
+ end
22
+ end
23
+
24
+ context 'invalid arguments' do
25
+ it 'raises an ArgumentError' do
26
+ expect { subclass.new('invalid-argument') }
27
+ .to raise_exception(ArgumentError)
28
+ end
29
+ end
30
+ end
31
+
32
+ context 'valid arguments' do
33
+ before do
34
+ @result = subclass.new(:id)
35
+ end
36
+
37
+ context 'return value' do
38
+ it 'is a an instance of the class' do
39
+ expect(@result).to be_an_instance_of(subclass)
40
+ end
41
+
42
+ it 'is a subclass of the described class' do
43
+ expect(@result.class.superclass).to eq(described_class)
44
+ end
45
+
46
+ it 'returns the same object for the same id' do
47
+ expect(@result).to equal(subclass.new(:id))
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ describe '::all' do
54
+ before { @result = subclass.all }
55
+
56
+ it 'returns an Array' do
57
+ expect(@result).to be_an(Array)
58
+ end
59
+
60
+ context 'it has not been instantiated' do
61
+ it 'returns an empty Array' do
62
+ expect(@result.count).to eq(0)
63
+ end
64
+ end
65
+
66
+ context 'it has been instantiated' do
67
+ before do
68
+ @instances = []
69
+ 3.times { |i| @instances.push(subclass.new(i.to_s.to_sym)) }
70
+ @result = subclass.all
71
+ end
72
+ after { clear_instance_metadata }
73
+
74
+ it 'returns an array of instances' do
75
+ expect(@result.first.class).to eq(subclass)
76
+ end
77
+
78
+ it 'returns all of the instances' do
79
+ expect(@result).to match_array(@instances)
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '::id' do
85
+ it 'returns a symbol' do
86
+ expect(subclass.id.class).to eq(Symbol)
87
+ end
88
+
89
+ context 'named subclass' do
90
+ before { class NamedClassExample < described_class; end }
91
+ after { Object.send(:remove_const, 'NamedClassExample') }
92
+
93
+ it 'returns a symbol version of the class name' do
94
+ expect(NamedClassExample.id).to eq(:named_class_example)
95
+ end
96
+ end
97
+ end
98
+
99
+ describe '#to_s' do
100
+ let(:instance) { subclass.new(:name) }
101
+
102
+ it 'returns a capitalized string' do
103
+ expect(instance.to_s).to eq('Name')
104
+ end
105
+ end
106
+ end
107
+
108
+ # This cleans up metadata about subclasses so that
109
+ # these specs can be run reliably in any order
110
+ def clear_instance_metadata
111
+ subclass.instance_variable_set(:@instances, {})
112
+ end
113
+ end