lemonade_stand 0.0.1

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.
@@ -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