lemonade_stand 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,31 @@
1
+ module LemonadeStand
2
+
3
+ class Result
4
+
5
+ attr_reader :glasses_sold
6
+ attr_reader :choice
7
+ attr_reader :day
8
+
9
+ def initialize data
10
+ @glasses_sold = data[:glasses_sold]
11
+ @choice = data[:choice]
12
+ @day = data[:day]
13
+ end
14
+
15
+ def income
16
+ (@glasses_sold * @choice.price_per_glass).round 2
17
+ end
18
+
19
+ def expenses
20
+ glasses_cost = @choice.glasses_made * @day.cost_per_glass
21
+ signs_cost = 15 * @choice.signs
22
+ (glasses_cost + signs_cost).round 2
23
+ end
24
+
25
+ def profit
26
+ (income - expenses).round 2
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,3 @@
1
+ module LemonadeStand
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,32 @@
1
+ module LemonadeStand
2
+
3
+ class Weather
4
+
5
+ def initialize type
6
+ @type = type
7
+ end
8
+
9
+ def method_missing(meth, *args, &blk)
10
+ meth.to_s == "#{@type}?"
11
+ end
12
+
13
+ def to_s
14
+ @type.to_s.split('_').map { |x| x == 'and' ? x : x.capitalize! }.join(' ')
15
+ end
16
+
17
+ def self.weather_for day
18
+ type = if day.number < 3
19
+ :sunny
20
+ else
21
+ case rand(10)
22
+ when 0..5 then :sunny
23
+ when 6..7 then :cloudy
24
+ else :hot_and_dry
25
+ end
26
+ end
27
+ new type
28
+ end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,116 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe LemonadeStand::Choice do
4
+
5
+ let(:choice) { LemonadeStand::Choice.new }
6
+
7
+ describe "calculate sales" do
8
+
9
+ describe "sales factor" do
10
+
11
+ [
12
+ [1, 51.6],
13
+ [2, 49.2],
14
+ [3, 46.8],
15
+ [7, 37.2],
16
+ [8, 34.8],
17
+ [9, 32.4],
18
+ [10, 30.0],
19
+ [11, 24.79],
20
+ [12, 20.83],
21
+ [16, 11.72],
22
+ [17, 10.38],
23
+ [18, 9.26],
24
+ ].map { |x| Struct.new(:price, :expected).new(*x) }.each do |example|
25
+
26
+ describe "price is #{example.price}" do
27
+
28
+ before do
29
+ choice.price_per_glass = example.price
30
+ end
31
+
32
+ it "should have a sales factor of #{example.expected}" do
33
+ choice.send(:sales_factor).round(2).must_equal example.expected
34
+ end
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ describe "signs factor" do
44
+ [
45
+ [0, 0],
46
+ [1, 0.39],
47
+ [2, 0.63],
48
+ [5, 0.92],
49
+ [10, 0.99],
50
+ [100, 1.0],
51
+ ].map { |x| Struct.new(:signs, :expected).new(*x) }.each do |example|
52
+
53
+ describe "purchased #{example.signs} signs" do
54
+
55
+ before do
56
+ choice.signs = example.signs
57
+ end
58
+
59
+ it "should return factor of #{example.expected}" do
60
+ choice.send(:signs_factor).round(2).must_equal example.expected
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+
67
+ end
68
+
69
+ describe "max sales" do
70
+ [
71
+ [1, 0, 51.60],
72
+ [1, 1, 71.90],
73
+ [1, 2, 84.22],
74
+ [1, 100, 103.20],
75
+ [9, 0, 32.40],
76
+ [9, 0, 32.40],
77
+ [9, 1, 45.15],
78
+ [9, 5, 62.14],
79
+ [9, 10, 64.58],
80
+ [9, 100, 64.80],
81
+ [16, 0, 11.72],
82
+ [16, 100, 23.44],
83
+ ].map { |x| Struct.new(:price_per_glass, :signs, :expected).new(*x) }.each do |example|
84
+
85
+ describe "#{example.price_per_glass} glasses with #{example.signs} signs" do
86
+
87
+ before do
88
+ choice.price_per_glass = example.price_per_glass
89
+ choice.signs = example.signs
90
+ end
91
+
92
+ it "should return max sales of #{example.expected}" do
93
+ choice.max_sales.round(2).must_equal example.expected
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+
100
+ end
101
+
102
+ describe "dropping decimal points" do
103
+ [:signs, :price_per_glass, :glasses_made].each do |property|
104
+ describe property do
105
+ it "drop the decimal points" do
106
+ choice = LemonadeStand::Choice.new
107
+ choice.send("#{property}=".to_sym, 1.01)
108
+ choice.send(property).must_equal 1
109
+ choice.send("#{property}=".to_sym, 2.99)
110
+ choice.send(property).must_equal 2
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ end
@@ -0,0 +1,241 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe LemonadeStand::Day do
4
+
5
+ let(:day) { LemonadeStand::Day.new }
6
+
7
+ describe "the weather" do
8
+
9
+ it "should pull the weather" do
10
+ weather = Object.new
11
+ LemonadeStand::Weather.stubs(:weather_for).with(day).returns weather
12
+ day.weather.must_be_same_as weather
13
+ end
14
+
15
+ it "should return the same projected weather report" do
16
+ weather = Object.new
17
+ LemonadeStand::Weather.expects(:weather_for).returns weather
18
+ day.weather
19
+ day.weather # this call will fail if called twice
20
+ end
21
+
22
+ end
23
+
24
+ describe "sales_for" do
25
+
26
+ let(:day) { LemonadeStand::Day.new }
27
+ let(:choice) { Object.new }
28
+
29
+ let(:glasses_sold) { Object.new }
30
+
31
+ before do
32
+ day.stubs(:calculate_glasses_sold).with(choice).returns glasses_sold
33
+ end
34
+
35
+ it "should return a result" do
36
+ result = day.sales_for choice
37
+ result.is_a?(LemonadeStand::Result).must_equal true
38
+ end
39
+
40
+ it "should return the calculated sales" do
41
+ result = day.sales_for choice
42
+ result.glasses_sold.must_equal glasses_sold
43
+ end
44
+
45
+ it "should pass along the day" do
46
+ result = day.sales_for choice
47
+ result.day.must_be_same_as day
48
+ end
49
+
50
+ it "should pass along the choice" do
51
+ result = day.sales_for choice
52
+ result.choice.must_be_same_as choice
53
+ end
54
+
55
+ end
56
+
57
+ describe "calculate glasses sold" do
58
+
59
+ let(:choice) { LemonadeStand::Choice.new }
60
+
61
+ let(:event) do
62
+ e = Object.new
63
+ # default to an event that does not modify the number
64
+ def e.modify choice
65
+ choice.max_sales
66
+ end
67
+ e
68
+ end
69
+
70
+ let(:day) do
71
+ d = LemonadeStand::Day.new
72
+ d.stubs(:event).returns event
73
+ d
74
+ end
75
+
76
+ describe "no glasses were made" do
77
+ before { choice.glasses_made = 0 }
78
+
79
+ describe "and the max sales for the day was 1" do
80
+ before { choice.stubs(:max_sales).returns 1 }
81
+
82
+ it "should report that no glasses were sold" do
83
+ day.calculate_glasses_sold(choice).must_equal 0
84
+ end
85
+ end
86
+
87
+ end
88
+
89
+ describe "2 glasses were made" do
90
+ before { choice.glasses_made = 2 }
91
+
92
+ describe "and max sales for the day was 3" do
93
+ before { choice.stubs(:max_sales).returns 3 }
94
+
95
+ it "should report that 2 glasses was sold" do
96
+ day.calculate_glasses_sold(choice).must_equal 2
97
+ end
98
+
99
+ describe "and the day's event modified the glasses to 3" do
100
+ before { event.stubs(:modify).with(choice).returns 3 }
101
+
102
+ it "should report that 2 were returned" do
103
+ day.calculate_glasses_sold(choice).must_equal 2
104
+ end
105
+ end
106
+
107
+ describe "and the day's event modified the glasses to 4" do
108
+ before { event.stubs(:modify).with(choice).returns 4 }
109
+
110
+ it "should report that 2 were returned" do
111
+ day.calculate_glasses_sold(choice).must_equal 2
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "and max sales for the day was 1" do
117
+ before { choice.stubs(:max_sales).returns 1 }
118
+
119
+ it "should report that 1 glass was sold" do
120
+ day.calculate_glasses_sold(choice).must_equal 1
121
+ end
122
+ end
123
+
124
+ end
125
+
126
+ describe "4 glasses were made" do
127
+ before { choice.glasses_made = 4 }
128
+
129
+ describe "and max sales for the day was 3" do
130
+ before { choice.stubs(:max_sales).returns 3 }
131
+
132
+ it "should report that 3 glasses was sold" do
133
+ day.calculate_glasses_sold(choice).must_equal 3
134
+ end
135
+
136
+ describe "and the day's event modified the glasses to 4" do
137
+ before { event.stubs(:modify).with(choice).returns 4 }
138
+
139
+ it "should report that 4 were returned" do
140
+ day.calculate_glasses_sold(choice).must_equal 4
141
+ end
142
+ end
143
+
144
+ describe "and the day's event modified the glasses to 5" do
145
+ before { event.stubs(:modify).with(choice).returns 5 }
146
+
147
+ it "should report that 4 were returned" do
148
+ day.calculate_glasses_sold(choice).must_equal 4
149
+ end
150
+ end
151
+ end
152
+
153
+ describe "and max sales for the day was 1" do
154
+ before { choice.stubs(:max_sales).returns 1 }
155
+
156
+ it "should report that 1 glass was sold" do
157
+ day.calculate_glasses_sold(choice).must_equal 1
158
+ end
159
+ end
160
+
161
+ end
162
+
163
+ describe "5.001 glasses were made" do
164
+ before { choice.glasses_made = 5.001 }
165
+
166
+ describe "and max sales for the day was 3.01" do
167
+ before { choice.stubs(:max_sales).returns 3.01 }
168
+
169
+ it "should report that 3 glasses was sold" do
170
+ day.calculate_glasses_sold(choice).must_equal 3
171
+ end
172
+
173
+ describe "and the day's event modified the glasses to 5.01" do
174
+ before { event.stubs(:modify).with(choice).returns 5.01 }
175
+
176
+ it "should report that 5 were returned" do
177
+ day.calculate_glasses_sold(choice).must_equal 5
178
+ end
179
+ end
180
+
181
+ describe "and the day's event modified the glasses to 5.002" do
182
+ before { event.stubs(:modify).with(choice).returns 5.002 }
183
+
184
+ it "should report that 5 were returned" do
185
+ day.calculate_glasses_sold(choice).must_equal 5
186
+ end
187
+ end
188
+ end
189
+
190
+ describe "and max sales for the day was 1.01" do
191
+ before { choice.stubs(:max_sales).returns 1.01 }
192
+
193
+ it "should report that 1 glass was sold" do
194
+ day.calculate_glasses_sold(choice).must_equal 1
195
+ end
196
+ end
197
+
198
+ end
199
+
200
+
201
+ end
202
+
203
+ describe "event" do
204
+
205
+ let(:event) { Object.new }
206
+
207
+ before { LemonadeStand::Event.stubs(:for).with(day).returns(event).then.returns(Object.new) }
208
+
209
+ it "should look up the event for the day" do
210
+ day.event.must_be_same_as event
211
+ end
212
+
213
+ it "should return the same day on every call" do
214
+ day.event.must_be_same_as day.event
215
+ end
216
+
217
+ end
218
+
219
+ describe "cost per glass" do
220
+
221
+ [
222
+ [1, 2],
223
+ [2, 2],
224
+ [3, 4],
225
+ [4, 4],
226
+ [5, 5],
227
+ [6, 5],
228
+ ].map { |x| Struct.new(:day_number, :cost).new *x }.each do |example|
229
+
230
+ describe "day #{example.day_number}" do
231
+ before { day.stubs(:number).returns example.day_number }
232
+ it "should equal #{example.cost}" do
233
+ day.cost_per_glass.must_equal example.cost
234
+ end
235
+ end
236
+
237
+ end
238
+
239
+ end
240
+
241
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe "display" do
4
+ [
5
+ [0, '$0.00'],
6
+ [1, '$0.01'],
7
+ [10, '$0.10'],
8
+ [100, '$1.00'],
9
+ [123, '$1.23'],
10
+ ].map { |a| Struct.new(:input, :expected).new *a }.each do |example|
11
+ describe "money" do
12
+ it "should convert #{example.input} to #{example.expected}" do
13
+ LemonadeStand::Display
14
+ .money(example.input).must_equal example.expected
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,306 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe LemonadeStand::Event do
4
+
5
+ describe "for" do
6
+
7
+ let(:day) { Struct.new(:weather).new weather }
8
+ let(:weather) { Object.new }
9
+
10
+ before do
11
+ weather.stubs(:cloudy?).returns false
12
+ weather.stubs(:hot_and_dry?).returns false
13
+ weather.stubs(:sunny?).returns false
14
+ end
15
+
16
+ describe "and the weather is cloudy" do
17
+
18
+ before { weather.stubs(:cloudy?).returns true }
19
+
20
+ it "should return a cloudy event" do
21
+ cloudy_event = Object.new
22
+ LemonadeStand::Event.stubs(:cloudy_event_for).with(day).returns cloudy_event
23
+ result = LemonadeStand::Event.for(day)
24
+ result.must_be_same_as cloudy_event
25
+ end
26
+
27
+ end
28
+
29
+ describe "and the weather is hot and dry" do
30
+
31
+ before { weather.stubs(:hot_and_dry?).returns true }
32
+
33
+ it "should return a hot and dry event" do
34
+ event = Object.new
35
+ LemonadeStand::Event.stubs(:hot_and_dry_event_for).with(day).returns event
36
+ result = LemonadeStand::Event.for(day)
37
+ result.must_be_same_as event
38
+ end
39
+
40
+ end
41
+
42
+ describe "a sunny day" do
43
+
44
+ before { weather.stubs(:sunny?).returns true }
45
+
46
+ it "should return a sunny event" do
47
+ event = Object.new
48
+ LemonadeStand::Event.stubs(:sunny_event_for).with(day).returns event
49
+ result = LemonadeStand::Event.for(day)
50
+ result.must_be_same_as event
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ describe "a sunny event" do
58
+
59
+ let(:day) { Struct.new(:number).new 0 }
60
+
61
+ (0...25).to_a.each do |random_value|
62
+ describe "random 0-99 is #{random_value}" do
63
+
64
+ before { LemonadeStand::Event.stubs(:rand).with(100).returns random_value }
65
+
66
+ [1, 2].each do |day_number|
67
+ describe "and the day number is #{day_number}" do
68
+
69
+ before { day.stubs(:number).returns day_number }
70
+
71
+ it "should return a normal work event" do
72
+ event = Object.new
73
+ LemonadeStand::Event.stubs(:build).with(:normal).returns event
74
+ result = LemonadeStand::Event.sunny_event_for day
75
+ result.must_be_same_as event
76
+ end
77
+
78
+ end
79
+ end
80
+
81
+ (3..10).to_a.each do |day_number|
82
+ describe "and the day number is #{day_number}" do
83
+
84
+ before { day.stubs(:number).returns day_number }
85
+
86
+ it "should return a normal event" do
87
+ event = Object.new
88
+ LemonadeStand::Event.stubs(:build).with(:street_work).returns event
89
+ result = LemonadeStand::Event.sunny_event_for day
90
+ result.must_be_same_as event
91
+ end
92
+
93
+ end
94
+ end
95
+
96
+ end
97
+ end
98
+
99
+ (25..100).to_a.each do |random_value|
100
+ describe "random 0-99 is #{random_value}" do
101
+ before { LemonadeStand::Event.stubs(:rand).with(100).returns random_value }
102
+
103
+ describe "and day is 3" do
104
+ before { day.number = 3 }
105
+
106
+ it "should return a normal day" do
107
+ event = Object.new
108
+ LemonadeStand::Event.stubs(:build).with(:normal).returns event
109
+ result = LemonadeStand::Event.sunny_event_for day
110
+ result.must_be_same_as event
111
+ end
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ describe "a cloudy event" do
120
+
121
+ let(:day) { Object.new }
122
+
123
+ (0...25).to_a.each do |random_value|
124
+ describe "random 0-99 is #{random_value}" do
125
+
126
+ before { LemonadeStand::Event.stubs(:rand).with(100).returns random_value }
127
+
128
+ it "should return a normal work event" do
129
+ event = Object.new
130
+ LemonadeStand::Event.stubs(:build).with(:storm).returns event
131
+ result = LemonadeStand::Event.cloudy_event_for day
132
+ result.must_be_same_as event
133
+ end
134
+
135
+ end
136
+ end
137
+
138
+ (25..100).to_a.each do |random_value|
139
+ describe "random 0-99 is #{random_value}" do
140
+
141
+ before { LemonadeStand::Event.stubs(:rand).with(100).returns random_value }
142
+
143
+ it "should return a normal day" do
144
+ event = Object.new
145
+ LemonadeStand::Event.stubs(:build).with(:rain).returns event
146
+ result = LemonadeStand::Event.cloudy_event_for day
147
+ result.must_be_same_as event
148
+ end
149
+
150
+ end
151
+ end
152
+
153
+ end
154
+
155
+ describe "a hot and dry event" do
156
+ it "should return a heat wave" do
157
+ event = Object.new
158
+ LemonadeStand::Event.stubs(:build).with(:heat_wave).returns event
159
+ result = LemonadeStand::Event.hot_and_dry_event_for nil
160
+ result.must_be_same_as event
161
+ end
162
+ end
163
+
164
+ describe "build" do
165
+ [
166
+ [:rain, LemonadeStand::RainEvent],
167
+ [:storm, LemonadeStand::StormEvent],
168
+ [:heat_wave, LemonadeStand::HeatWaveEvent],
169
+ [:street_work, LemonadeStand::StreetWorkEvent],
170
+ [:normal, LemonadeStand::NormalEvent],
171
+ ].map { |x| Struct.new(:type, :klass).new *x }.each do |example|
172
+ describe "building #{example.type}" do
173
+ it "should create an #{example.klass}" do
174
+ LemonadeStand::Event.build(example.type)
175
+ .is_a?(example.klass).must_equal true
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ [
182
+ LemonadeStand::RainEvent,
183
+ LemonadeStand::StormEvent,
184
+ LemonadeStand::HeatWaveEvent,
185
+ LemonadeStand::StreetWorkEvent,
186
+ LemonadeStand::NormalEvent,
187
+ ].each do |type|
188
+ describe type do
189
+ it "should be an event" do
190
+ type.new
191
+ .is_a?(LemonadeStand::Event)
192
+ .must_equal true
193
+ end
194
+ end
195
+ end
196
+
197
+ end
198
+
199
+ describe LemonadeStand::NormalEvent do
200
+
201
+ describe "modify" do
202
+
203
+ let(:choice) { Struct.new(:max_sales).new Object.new }
204
+
205
+ it "should return the default max sales" do
206
+ event = LemonadeStand::NormalEvent.new
207
+ result = event.modify choice
208
+ result.must_be_same_as choice.max_sales
209
+ end
210
+
211
+ end
212
+
213
+ end
214
+
215
+
216
+ describe LemonadeStand::HeatWaveEvent do
217
+ [
218
+ [1, 2],
219
+ [2, 4],
220
+ [3, 6],
221
+ ].map { |x| Struct.new(:max_sales, :expected).new *x }.each do |example|
222
+ describe "multiple examples" do
223
+ it "should double the maximum sales" do
224
+ choice = Struct.new(:max_sales).new example.max_sales
225
+ result = LemonadeStand::HeatWaveEvent.new.modify choice
226
+ result.must_equal example.expected
227
+ end
228
+ end
229
+ end
230
+ end
231
+
232
+ describe LemonadeStand::HeatWaveEvent do
233
+ it "should return 0" do
234
+ result = LemonadeStand::StormEvent.new.modify nil
235
+ result.must_equal 0
236
+ end
237
+ end
238
+
239
+ describe LemonadeStand::StreetWorkEvent do
240
+
241
+ let(:choice) { Struct.new(:glasses_made).new Object.new }
242
+ let(:event) { LemonadeStand::StreetWorkEvent.new }
243
+
244
+ (0...2).to_a.each do |random_value|
245
+ describe "random 0-4 is #{random_value}" do
246
+ before { event.stubs(:rand).with(4).returns random_value }
247
+ it "should return 0" do
248
+ result = event.modify choice
249
+ result.must_equal 0
250
+ end
251
+ end
252
+ end
253
+
254
+ describe "random 0-4 is 3" do
255
+ before { event.stubs(:rand).with(4).returns 3 }
256
+ it "should return the glasses sold" do
257
+ result = event.modify choice
258
+ result.must_be_same_as choice.glasses_made
259
+ end
260
+ end
261
+ end
262
+
263
+ describe LemonadeStand::RainEvent do
264
+
265
+ let(:choice) { Struct.new(:glasses_made).new Object.new }
266
+ let(:event) { LemonadeStand::RainEvent.new }
267
+
268
+ [
269
+ [0.0001, 0.30, 10, 7],
270
+ [0.9999, 0.80, 10, 2],
271
+ [0.0329, 0.32, 100, 68],
272
+ [0.5, 0.55, 100, 45],
273
+ ].map { |x| Struct.new(:random, :chance_of_rain, :previous_max_sales, :new_max_sales).new *x }.each do |example|
274
+
275
+ describe "chance of rain" do
276
+
277
+ before { event.stubs(:rand).returns example.random }
278
+
279
+ it "should calculate the chance of rain (#{example.chance_of_rain})" do
280
+ event.chance_of_rain.must_equal example.chance_of_rain
281
+ end
282
+
283
+ end
284
+
285
+ describe "calculating sales for the day" do
286
+
287
+ before do
288
+ event.stubs(:rand).returns example.random
289
+ choice.stubs(:max_sales).returns example.previous_max_sales
290
+ end
291
+
292
+ it "should alter the max sales according to the chance of rain" do
293
+ result = event.modify choice
294
+ result.must_equal example.new_max_sales
295
+ end
296
+
297
+ it "should return an integer" do
298
+ result = event.modify choice
299
+ result.class.must_equal Fixnum
300
+ end
301
+
302
+ end
303
+
304
+ end
305
+
306
+ end