zillion 0.0.8 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6ce05db824e474efb57c076724e898b942e9d1e7
4
- data.tar.gz: 4b9cd7809dfe54aebde95a44e20c367d5fecdd1e
3
+ metadata.gz: 91b941a168263933e9486bb2e3ba9dacf45b3d65
4
+ data.tar.gz: 6eafd20870092fa550a2f44e816d9776660c5e51
5
5
  SHA512:
6
- metadata.gz: 5474bb4ab5fbec4aa0dfdb1d8d6739086760b76737534e934b8b8149e45a046acc3b9ae48d2262fdb9a90e1522f91da0fb016b48baa7f35c49765f9d23360df6
7
- data.tar.gz: 8034f57e84d372f1209fc6f074c39661ee21a810860cf2c250d6f968c280ffd5a69ec5d5bf9d3c4944c8b1f3b5c5792fec183591475a321ca5f22aa58c1c5a91
6
+ metadata.gz: 0e4be1ce406af366e61cfe47433d3a0f33637bf548bd1ac7706d9359397b062a7effa77909cd4b89a381452bbb6a86849481f083b45f1781f7a2a9327521a31e
7
+ data.tar.gz: 95e9d521a36ec5f21d4c921fc6693b39f379076eeb7f33f3a7c8a9c8414619a1a8fdc154eaffeaaccd4a5ade8670761ec99753c236e5a36d82a8d15cf41d9d71
data/lib/zillion/plan.rb CHANGED
@@ -128,16 +128,16 @@ class TieredPlan < Plan
128
128
  def amounts_for(counts)
129
129
  tier = matching_tier_for(counts)
130
130
  obj = {}
131
- obj[tier.last.to_sym] = tier[1]
131
+ obj[tier.last.to_sym] = tier[1] # tier[1] is monthly cost
132
132
  obj
133
133
  end
134
134
 
135
135
  def matching_tier_for(counts)
136
136
  if tiers = matching_tiers_for(counts) and tiers.any?
137
- # raise "More than one tier matches for #{counts}" if tiers.count > 1
138
- tiers.first
137
+ # sort by monthly cost and return highest (first) one
138
+ tiers.sort { |a, b| a[1] <=> b[1] }.last
139
139
  else
140
- raise "No matching tiers found for #{counts.inspect} in #{name} plan."
140
+ raise TierNotFoundError.new("No matching tiers found for #{counts.inspect} in #{name} plan.")
141
141
  end
142
142
  end
143
143
 
@@ -148,11 +148,6 @@ class TieredPlan < Plan
148
148
 
149
149
  private
150
150
 
151
- def matching_tiers_for(counts)
152
- # sort by amount (format is [range, monthly_cost, index, type])
153
- get_matching_tiers(counts).sort { |a, b| b[1] <=> a[1] }
154
- end
155
-
156
151
  def find_tier_for(tiers, units)
157
152
  # raise "Units not set!" unless units
158
153
  arr = tiers.select { |t| t[0].include?(units) }
@@ -168,10 +163,10 @@ class TieredPlan < Plan
168
163
 
169
164
  def tier_groups
170
165
  spec[:tiers].each_with_object({}) do |(key, list), obj|
171
- obj[key] = list.map do |t|
166
+ obj[key.to_sym] = list.map do |t|
172
167
  h = symbolize_keys(t)
173
168
  [ h[:from]..h[:to], h[tier_cost_key] ]
174
- end
169
+ end.sort { |a, b| a[0].last <=> b[0].last } # sort by last/highest item in range
175
170
  end
176
171
  end
177
172
 
@@ -179,10 +174,22 @@ class TieredPlan < Plan
179
174
  :monthly_cost
180
175
  end
181
176
 
182
- def get_matching_tiers(counts)
177
+ # returns the last/highest existing tiers for the given keys
178
+ def last_existing_tier_for(key)
179
+ if found = tier_groups[key.to_sym]
180
+ found.last + [key]
181
+ end
182
+ end
183
+
184
+ def matching_tiers_for(counts, fallback_to_highest_tier = false)
183
185
  tier_groups.map do |key, tiers|
184
- units = counts[key] or raise "Unit count required for #{key}"
185
- find_tier_for(tiers, units.to_i) + [key]
186
+ units = counts[key] or raise MissingCountError.new("Unit count required for #{key}")
187
+ begin
188
+ find_tier_for(tiers, units.to_i) + [key]
189
+ rescue TierNotFoundError
190
+ raise unless fallback_to_highest_tier
191
+ last_existing_tier_for(key)
192
+ end
186
193
  end.compact
187
194
  end
188
195
 
@@ -194,11 +201,11 @@ end
194
201
 
195
202
  class MeteredPlan < TieredPlan
196
203
 
197
- def amounts_for(counts)
204
+ def amounts_for(counts, fallback_to_highest_tier = false)
198
205
  obj = {}
199
- matching_tiers_for(counts).map do |tier|
206
+ matching_tiers_for(counts, fallback_to_highest_tier).map do |tier|
200
207
  units = units_for(tier.last, counts)
201
- obj[tier.last.to_sym] = units * tier[1]
208
+ obj[tier.last.to_sym] = units * tier[1] # tier[1] is monthly unit cost
202
209
  end
203
210
  obj
204
211
  end
@@ -207,6 +214,8 @@ class MeteredPlan < TieredPlan
207
214
  counts[type]
208
215
  end
209
216
 
217
+ private
218
+
210
219
  def tier_cost_key
211
220
  :monthly_unit_cost
212
221
  end
@@ -219,7 +228,7 @@ class FixedMeteredPlan < MeteredPlan
219
228
  spec[:monthly_fee] ? spec[:monthly_fee].to_f : 0
220
229
  end
221
230
 
222
- def amounts_for(counts)
231
+ def amounts_for(counts, fallback_to_highest_tier = false)
223
232
  obj = super
224
233
  obj[:base_fee] = base_fee
225
234
  obj
@@ -256,6 +265,9 @@ class FixedMeteredPlan < MeteredPlan
256
265
  # so we should reduce the count of actual units
257
266
  units -= limits[type] if limits[type]
258
267
 
268
+ # but ensure we never go below zero
269
+ units = 0 if units < 0
270
+
259
271
  units
260
272
  end
261
273
 
@@ -263,12 +275,17 @@ class FixedMeteredPlan < MeteredPlan
263
275
  :monthly_unit_cost
264
276
  end
265
277
 
266
- def get_matching_tiers(counts)
278
+ def matching_tiers_for(counts, fallback_to_highest_tier = false)
267
279
  tier_groups.map do |key, tiers|
268
- units = counts[key] or raise "Unit count required for #{key}"
280
+ units = counts[key] or raise MissingCountError.new("Unit count required for #{key}")
269
281
  next if limits[key] and limits[key] >= units.to_i
270
282
 
271
- find_tier_for(tiers, units.to_i) + [key]
283
+ begin
284
+ find_tier_for(tiers, units.to_i) + [key]
285
+ rescue TierNotFoundError
286
+ raise unless fallback_to_highest_tier
287
+ last_existing_tier_for(key)
288
+ end
272
289
  end.compact
273
290
  end
274
291
 
data/spec/plans_spec.rb CHANGED
@@ -39,48 +39,48 @@ describe 'FreePlan' do
39
39
 
40
40
  end
41
41
 
42
- describe '#monthly_fee_for(counts)' do
42
+ describe '#available_for?(counts)' do
43
43
 
44
- it 'returns 0 if no limits' do
45
- expect(plan.monthly_fee_for({ apples: 10 })).to eql(0)
44
+ it 'returns true if no limits' do
45
+ expect(plan.available_for?({ bananas: 100 }) ).to eql(true)
46
46
  end
47
47
 
48
- it 'returns 0 if within limits' do
48
+ it 'raises if not all units for limits are fed' do
49
49
  other = FreePlan.new(limits: { bananas: 10 })
50
- expect(other.monthly_fee_for({ bananas: 10 })).to eql(0)
50
+ expect { other.available_for?({ apples: 10 }) }.to raise_error(Zillion::Plan::MissingCountError)
51
51
  end
52
52
 
53
- it 'raises if not within limits' do
53
+ it 'returns false if limits/units match, but over the limit' do
54
54
  other = FreePlan.new(limits: { bananas: 10 })
55
- expect { other.monthly_fee_for({ bananas: 11 }) }.to raise_error
55
+ expect(other.available_for?({ bananas: 20 })).to eql(false)
56
56
  end
57
57
 
58
- it 'raises if not all counts present' do
58
+ it 'returns true if limits/units match, and within the limit' do
59
59
  other = FreePlan.new(limits: { bananas: 10, apples: 10 })
60
- expect { other.monthly_fee_for({ apples: 5 }) }.to raise_error
60
+ expect(other.available_for?({ bananas: 5, apples: 2 })).to eql(true)
61
61
  end
62
62
 
63
63
  end
64
64
 
65
- describe '#available_for?(counts)' do
65
+ describe '#monthly_fee_for(counts)' do
66
66
 
67
- it 'returns true if no limits' do
68
- expect(plan.available_for?({ bananas: 100 }) ).to eql(true)
67
+ it 'returns 0 if no limits' do
68
+ expect(plan.monthly_fee_for({ apples: 10 })).to eql(0)
69
69
  end
70
70
 
71
- it 'raises if not all units for limits are fed' do
71
+ it 'returns 0 if within limits' do
72
72
  other = FreePlan.new(limits: { bananas: 10 })
73
- expect { other.available_for?({ apples: 10 }) }.to raise_error
73
+ expect(other.monthly_fee_for({ bananas: 10 })).to eql(0)
74
74
  end
75
75
 
76
- it 'returns false if limits/units match, but over the limit' do
76
+ it 'raises if not within limits' do
77
77
  other = FreePlan.new(limits: { bananas: 10 })
78
- expect(other.available_for?({ bananas: 20 })).to eql(false)
78
+ expect { other.monthly_fee_for({ bananas: 11 }) }.to raise_error(Zillion::Plan::OverLimitsError)
79
79
  end
80
80
 
81
- it 'returns true if limits/units match, and within the limit' do
81
+ it 'raises if not all counts present' do
82
82
  other = FreePlan.new(limits: { bananas: 10, apples: 10 })
83
- expect(other.available_for?({ bananas: 5, apples: 2 })).to eql(true)
83
+ expect { other.monthly_fee_for({ apples: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
84
84
  end
85
85
 
86
86
  end
@@ -95,7 +95,7 @@ describe 'FixedPlan' do
95
95
 
96
96
  it 'raises if monthly_fee not set' do
97
97
  other = FixedPlan.new(limits: { bananas: 10 })
98
- expect { other.monthly_fee }.to raise_error
98
+ expect { other.monthly_fee }.to raise_error(Zillion::Plan::InvalidFeeError)
99
99
  end
100
100
 
101
101
  it 'returns monthly_fee, if present' do
@@ -104,48 +104,48 @@ describe 'FixedPlan' do
104
104
 
105
105
  end
106
106
 
107
- describe '#monthly_fee_for(counts)' do
107
+ describe '#available_for?(counts)' do
108
108
 
109
- it 'returns monthly_fee if no limits' do
110
- expect(plan.monthly_fee_for({ apples: 10 })).to eql(10.0)
109
+ it 'returns true if no limits' do
110
+ expect(plan.available_for?({ bananas: 100 }) ).to eql(true)
111
111
  end
112
112
 
113
- it 'returns monthly_fee if within limits' do
113
+ it 'raises not all units for limits are fed' do
114
114
  other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10 })
115
- expect(other.monthly_fee_for({ bananas: 10 })).to eql(10.0)
115
+ expect { other.available_for?({ apples: 10 }) }.to raise_error
116
116
  end
117
117
 
118
- it 'raises if not within limits' do
118
+ it 'returns false if limits/units match, but over the limit' do
119
119
  other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10 })
120
- expect { other.monthly_fee_for({ bananas: 11 }) }.to raise_error
120
+ expect(other.available_for?({ bananas: 20 })).to eql(false)
121
121
  end
122
122
 
123
- it 'raises if not all counts present' do
123
+ it 'returns true if limits/units match, and within the limit' do
124
124
  other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10, apples: 10 })
125
- expect { other.monthly_fee_for({ apples: 5 }) }.to raise_error
125
+ expect(other.available_for?({ bananas: 5, apples: 2 })).to eql(true)
126
126
  end
127
127
 
128
128
  end
129
129
 
130
- describe '#available_for?(counts)' do
130
+ describe '#monthly_fee_for(counts)' do
131
131
 
132
- it 'returns true if no limits' do
133
- expect(plan.available_for?({ bananas: 100 }) ).to eql(true)
132
+ it 'returns monthly_fee if no limits' do
133
+ expect(plan.monthly_fee_for({ apples: 10 })).to eql(10.0)
134
134
  end
135
135
 
136
- it 'raises not all units for limits are fed' do
136
+ it 'returns monthly_fee if within limits' do
137
137
  other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10 })
138
- expect { other.available_for?({ apples: 10 }) }.to raise_error
138
+ expect(other.monthly_fee_for({ bananas: 10 })).to eql(10.0)
139
139
  end
140
140
 
141
- it 'returns false if limits/units match, but over the limit' do
141
+ it 'raises if not within limits' do
142
142
  other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10 })
143
- expect(other.available_for?({ bananas: 20 })).to eql(false)
143
+ expect { other.monthly_fee_for({ bananas: 11 }) }.to raise_error(Zillion::Plan::OverLimitsError)
144
144
  end
145
145
 
146
- it 'returns true if limits/units match, and within the limit' do
146
+ it 'raises if not all counts present' do
147
147
  other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10, apples: 10 })
148
- expect(other.available_for?({ bananas: 5, apples: 2 })).to eql(true)
148
+ expect { other.monthly_fee_for({ apples: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
149
149
  end
150
150
 
151
151
  end
@@ -160,8 +160,8 @@ describe 'TieredPlan', 'Single tier' do
160
160
  limits: { bananas: 10 },
161
161
  tiers: {
162
162
  products: [
163
- { from: 0, to: 100, monthly_cost: 20 },
164
163
  { from: 101, to: 200, monthly_cost: 30 },
164
+ { from: 0, to: 100, monthly_cost: 20 },
165
165
  ]
166
166
  })
167
167
  }
@@ -174,42 +174,42 @@ describe 'TieredPlan', 'Single tier' do
174
174
 
175
175
  end
176
176
 
177
- describe '#monthly_fee_for(counts)' do
177
+ describe '#available_for?(counts)' do
178
178
 
179
179
  it 'raises if missing unit for tier' do
180
- expect { plan.monthly_fee_for({ bananas: 5 }) }.to raise_error
180
+ expect { plan.available_for?({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
181
181
  end
182
182
 
183
183
  it 'raises if missing unit for fixed limit' do
184
- expect { plan.monthly_fee_for({ products: 25 }) }.to raise_error
184
+ expect { plan.available_for?({ products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
185
185
  end
186
186
 
187
187
  it 'raises if not within limits' do
188
- expect { plan.monthly_fee_for({ bananas: 5, products: -5 }) }.to raise_error
188
+ expect { plan.available_for?({ bananas: 5, products: -5 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
189
189
  end
190
190
 
191
191
  it 'returns monthly fee for tier if within its limit' do
192
- expect(plan.monthly_fee_for({ bananas: 5, products: 25 })).to eql(20)
192
+ expect(plan.available_for?({ bananas: 5, products: 25 })).to eql(true)
193
193
  end
194
194
 
195
195
  end
196
196
 
197
- describe '#available_for?(counts)' do
197
+ describe '#monthly_fee_for(counts)' do
198
198
 
199
199
  it 'raises if missing unit for tier' do
200
- expect { plan.available_for?({ bananas: 5 }) }.to raise_error
200
+ expect { plan.monthly_fee_for({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
201
201
  end
202
202
 
203
203
  it 'raises if missing unit for fixed limit' do
204
- expect { plan.available_for?({ products: 25 }) }.to raise_error
204
+ expect { plan.monthly_fee_for({ products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
205
205
  end
206
206
 
207
207
  it 'raises if not within limits' do
208
- expect { plan.available_for?({ bananas: 5, products: -5 }) }.to raise_error
208
+ expect { plan.monthly_fee_for({ bananas: 5, products: -5 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
209
209
  end
210
210
 
211
211
  it 'returns monthly fee for tier if within its limit' do
212
- expect(plan.available_for?({ bananas: 5, products: 25 })).to eql(true)
212
+ expect(plan.monthly_fee_for({ bananas: 5, products: 25 })).to eql(20)
213
213
  end
214
214
 
215
215
  end
@@ -223,8 +223,8 @@ describe 'TieredPlan', 'Multiple tiers' do
223
223
  limits: { bananas: 10 },
224
224
  tiers: {
225
225
  orders: [
226
+ { from: 11, to: 20, monthly_cost: 35 },
226
227
  { from: 0, to: 10, monthly_cost: 15 },
227
- { from: 11, to: 20, monthly_cost: 35 }
228
228
  ],
229
229
  products: [
230
230
  { from: 0, to: 100, monthly_cost: 20 },
@@ -241,42 +241,42 @@ describe 'TieredPlan', 'Multiple tiers' do
241
241
 
242
242
  end
243
243
 
244
- describe '#monthly_fee_for(counts)' do
244
+ describe '#available_for?(counts)' do
245
245
 
246
246
  it 'raises if missing unit for tier' do
247
- expect { plan.monthly_fee_for({ bananas: 5, products: 25 }) }.to raise_error
247
+ expect { plan.available_for?({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
248
248
  end
249
249
 
250
250
  it 'raises if missing unit for fixed limit' do
251
- expect { plan.monthly_fee_for({ products: 25 }) }.to raise_error
251
+ expect { plan.available_for?({ products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
252
252
  end
253
253
 
254
254
  it 'raises if not within limits' do
255
- expect { plan.monthly_fee_for({ bananas: 5, products: -5 }) }.to raise_error
255
+ expect { plan.available_for?({ bananas: 5, products: -5, orders: -1 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
256
256
  end
257
257
 
258
- it 'returns monthly fee for tier if within its limit' do
259
- expect(plan.monthly_fee_for({ bananas: 1, orders: 5, products: 25 })).to eql(20)
258
+ it 'returns true if within tier limits' do
259
+ expect(plan.available_for?({ bananas: 5, orders: 5, products: 25 })).to eql(true)
260
260
  end
261
261
 
262
262
  end
263
263
 
264
- describe '#available_for?(counts)' do
264
+ describe '#monthly_fee_for(counts)' do
265
265
 
266
266
  it 'raises if missing unit for tier' do
267
- expect { plan.available_for?({ bananas: 5 }) }.to raise_error
267
+ expect { plan.monthly_fee_for({ bananas: 5, products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
268
268
  end
269
269
 
270
270
  it 'raises if missing unit for fixed limit' do
271
- expect { plan.available_for?({ products: 25 }) }.to raise_error
271
+ expect { plan.monthly_fee_for({ products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
272
272
  end
273
273
 
274
274
  it 'raises if not within limits' do
275
- expect { plan.available_for?({ bananas: 5, products: -5 }) }.to raise_error
275
+ expect { plan.monthly_fee_for({ bananas: 5, products: -5, orders: 0 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
276
276
  end
277
277
 
278
- it 'returns true if within tier limits' do
279
- expect(plan.available_for?({ bananas: 5, orders: 5, products: 25 })).to eql(true)
278
+ it 'returns monthly fee for tier if within its limit' do
279
+ expect(plan.monthly_fee_for({ bananas: 1, orders: 5, products: 25 })).to eql(20)
280
280
  end
281
281
 
282
282
  end
@@ -290,8 +290,8 @@ describe 'MeteredPlan', 'Single tier' do
290
290
  limits: { bananas: 10 },
291
291
  tiers: {
292
292
  products: [
293
+ { from: 101, to: 200, monthly_unit_cost: 1.5 },
293
294
  { from: 0, to: 100, monthly_unit_cost: 2 },
294
- { from: 101, to: 200, monthly_unit_cost: 1.5 }
295
295
  ]
296
296
  })
297
297
  }
@@ -304,18 +304,38 @@ describe 'MeteredPlan', 'Single tier' do
304
304
 
305
305
  end
306
306
 
307
+ describe '#available_for?(counts)' do
308
+
309
+ it 'raises if missing unit for tier' do
310
+ expect { plan.available_for?({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
311
+ end
312
+
313
+ it 'raises if missing unit for fixed limit' do
314
+ expect { plan.available_for?({ products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
315
+ end
316
+
317
+ it 'raises if not within limits' do
318
+ expect { plan.available_for?({ bananas: 5, products: -5 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
319
+ end
320
+
321
+ it 'returns true if within its limit' do
322
+ expect(plan.available_for?({ bananas: 5, products: 25 })).to eql(true)
323
+ end
324
+
325
+ end
326
+
307
327
  describe '#monthly_fee_for(counts)' do
308
328
 
309
329
  it 'raises if missing unit for tier' do
310
- expect { plan.monthly_fee_for({ bananas: 5 }) }.to raise_error
330
+ expect { plan.monthly_fee_for({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
311
331
  end
312
332
 
313
333
  it 'raises if missing unit for fixed limit' do
314
- expect { plan.monthly_fee_for({ products: 25 }) }.to raise_error
334
+ expect { plan.monthly_fee_for({ products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
315
335
  end
316
336
 
317
337
  it 'raises if not within limits' do
318
- expect { plan.monthly_fee_for({ bananas: 5, products: -5 }) }.to raise_error
338
+ expect { plan.monthly_fee_for({ bananas: 5, products: -5 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
319
339
  end
320
340
 
321
341
  it 'returns monthly fee for tier if within its limit' do
@@ -324,26 +344,34 @@ describe 'MeteredPlan', 'Single tier' do
324
344
 
325
345
  end
326
346
 
327
- describe '#available_for?(counts)' do
347
+ describe '#amounts_for(counts)' do
328
348
 
329
349
  it 'raises if missing unit for tier' do
330
- expect { plan.available_for?({ bananas: 5 }) }.to raise_error
350
+ expect { plan.amounts_for({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
331
351
  end
332
352
 
333
- it 'raises if missing unit for fixed limit' do
334
- expect { plan.available_for?({ products: 25 }) }.to raise_error
353
+ it 'raises if not within limits' do
354
+ expect { plan.amounts_for({ bananas: 5, products: -5 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
355
+ end
356
+
357
+ it 'returns amounts if within limits' do
358
+ expect(plan.amounts_for({ products: 25 })).to eql({ products: 50 })
335
359
  end
336
360
 
337
361
  it 'raises if not within limits' do
338
- expect { plan.available_for?({ bananas: 5, products: -5 }) }.to raise_error
362
+ expect { plan.amounts_for({ products: 250 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
339
363
  end
340
364
 
341
- it 'returns true if within its limit' do
342
- expect(plan.available_for?({ bananas: 5, products: 25 })).to eql(true)
365
+ it 'doesnt raise if not within limits but second arg is true' do
366
+ expect { plan.amounts_for({ products: 250 }, true) }.not_to raise_error(Zillion::Plan::TierNotFoundError)
367
+ expect(plan.amounts_for({ products: 250 }, true)).to eql({ products: 375.0 })
343
368
  end
344
369
 
345
- end
370
+ it 'returns amounts if within limits' do
371
+ expect(plan.amounts_for({ bananas: 5, products: 25 })).to eql({ products: 50 })
372
+ end
346
373
 
374
+ end
347
375
 
348
376
  end
349
377
 
@@ -372,18 +400,43 @@ describe 'MeteredPlan', 'Multiple tiers' do
372
400
 
373
401
  end
374
402
 
375
- describe '#monthly_fee_for(counts)' do
403
+
404
+ describe '#available_for?(counts)' do
376
405
 
377
406
  it 'raises if missing unit for tier' do
378
- expect { plan.monthly_fee_for({ bananas: 5 }) }.to raise_error
407
+ expect { plan.available_for?({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
379
408
  end
380
409
 
381
410
  it 'raises if missing unit for fixed limit' do
382
- expect { plan.monthly_fee_for({ products: 25 }) }.to raise_error
411
+ expect { plan.available_for?({ products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
383
412
  end
384
413
 
385
414
  it 'raises if not within limits' do
386
- expect { plan.monthly_fee_for({ bananas: 5, products: -5 }) }.to raise_error
415
+ expect { plan.available_for?({ bananas: 5, products: -5, orders: 1 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
416
+ end
417
+
418
+ it 'returns true if within its limit' do
419
+ expect(plan.available_for?({ bananas: 5, orders: 5, products: 25 })).to eql(true)
420
+ end
421
+
422
+ end
423
+
424
+ describe '#monthly_fee_for(counts)' do
425
+
426
+ it 'raises if missing unit for tier' do
427
+ expect { plan.monthly_fee_for({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
428
+ end
429
+
430
+ it 'raises if missing unit for fixed limit' do
431
+ expect { plan.monthly_fee_for({ products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
432
+ end
433
+
434
+ it 'raises if below limits' do
435
+ expect { plan.monthly_fee_for({ bananas: 5, products: -5, orders: 10 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
436
+ end
437
+
438
+ it 'raises if over limits' do
439
+ expect { plan.monthly_fee_for({ bananas: 5, products: 250, orders: 10 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
387
440
  end
388
441
 
389
442
  it 'returns monthly fee for tier if within its limit' do
@@ -392,22 +445,31 @@ describe 'MeteredPlan', 'Multiple tiers' do
392
445
 
393
446
  end
394
447
 
395
- describe '#available_for?(counts)' do
448
+ describe '#amounts_for(counts)' do
396
449
 
397
450
  it 'raises if missing unit for tier' do
398
- expect { plan.available_for?({ bananas: 5 }) }.to raise_error
451
+ expect { plan.amounts_for({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
399
452
  end
400
453
 
401
- it 'raises if missing unit for fixed limit' do
402
- expect { plan.available_for?({ products: 25 }) }.to raise_error
454
+ it 'raises if not within limits' do
455
+ expect { plan.amounts_for({ bananas: 5, products: -5, orders: 1 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
403
456
  end
404
457
 
405
458
  it 'raises if not within limits' do
406
- expect { plan.available_for?({ bananas: 5, products: -5 }) }.to raise_error
459
+ expect { plan.amounts_for({ products: 250, orders: 5, orders: 1 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
407
460
  end
408
461
 
409
- it 'returns true if within its limit' do
410
- expect(plan.available_for?({ bananas: 5, orders: 5, products: 25 })).to eql(true)
462
+ it 'doesnt raise if not within limits but second arg is true' do
463
+ expect { plan.amounts_for({ products: 250, orders: 5 }, true) }.not_to raise_error(Zillion::Plan::TierNotFoundError)
464
+ expect(plan.amounts_for({ products: 250, orders: 5 }, true)).to eql({ orders: 15, products: 375.0 })
465
+ end
466
+
467
+ it 'returns amounts if within limits' do
468
+ expect(plan.amounts_for({ bananas: 5, products: 25, orders: 5 })).to eql({ orders: 15, products: 50 })
469
+ end
470
+
471
+ it 'returns amounts if within limits' do
472
+ expect(plan.amounts_for({ bananas: 5, products: 25, orders: 1 })).to eql({ orders: 3, products: 50 })
411
473
  end
412
474
 
413
475
  end
@@ -422,8 +484,8 @@ describe 'FixedMeteredPlan' do
422
484
  limits: { bananas: 10, orders: 10, products: 10 },
423
485
  tiers: {
424
486
  orders: [
487
+ { from: 16, to: 20, monthly_unit_cost: 1 },
425
488
  { from: 11, to: 15, monthly_unit_cost: 3 },
426
- { from: 16, to: 20, monthly_unit_cost: 1 }
427
489
  ],
428
490
  products: [
429
491
  { from: 5, to: 100, monthly_unit_cost: 2 },
@@ -443,14 +505,53 @@ describe 'FixedMeteredPlan' do
443
505
 
444
506
  end
445
507
 
508
+
509
+ describe '#available_for?(counts)' do
510
+
511
+ it 'raises if missing units for tier' do
512
+ expect { plan.available_for?({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
513
+ end
514
+
515
+ it 'raises if missing unit for fixed limit' do
516
+ expect { plan.available_for?({ products: 25 }) }.to raise_error(Zillion::Plan::MissingCountError)
517
+ end
518
+
519
+ it 'doent explode if negative key for limit with tier' do
520
+ expect(plan.available_for?({ bananas: 5, orders: 5, products: -5 })).to eql(true)
521
+ end
522
+
523
+ it 'returns true if below fixed limits' do
524
+ expect(plan.available_for?({ bananas: 5, orders: 5, products: 5 })).to eql(true)
525
+ end
526
+
527
+ it 'returns true if below fixed limits, even if no matching tier exists' do
528
+ plan_opts[:tiers].delete(:products)
529
+ expect(plan.available_for?({ bananas: 5, orders: 5, products: 5 })).to eql(true)
530
+ end
531
+
532
+ it 'returns false if above fixed limit and no tier found' do
533
+ plan_opts[:tiers].delete(:products)
534
+ expect(plan.available_for?({ bananas: 5, orders: 5, products: 15 })).to eql(false)
535
+ end
536
+
537
+ it 'returns true if above fixed limit but within tier limit' do
538
+ expect(plan.available_for?({ bananas: 5, orders: 5, products: 15 })).to eql(true)
539
+ end
540
+
541
+ it 'returns true if above fixed limit but within tier limit' do
542
+ expect(plan.available_for?({ bananas: 5, orders: 15, products: 25 })).to eql(true)
543
+ end
544
+
545
+ end
546
+
446
547
  describe '#monthly_fee_for(counts)' do
447
548
 
448
549
  it 'raises if missing unit for tier' do
449
- expect { plan.monthly_fee_for({ bananas: 5 }) }.to raise_error
550
+ expect { plan.monthly_fee_for({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
450
551
  end
451
552
 
452
553
  it 'raises if missing unit for fixed limit' do
453
- expect { plan.monthly_fee_for({ products: 5 }) }.to raise_error
554
+ expect { plan.monthly_fee_for({ products: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
454
555
  end
455
556
 
456
557
  it 'doent explode if negative key for tiered count, but under fixed limit' do
@@ -461,7 +562,7 @@ describe 'FixedMeteredPlan' do
461
562
  expect(plan.monthly_fee_for({ bananas: 5, orders: 5, products: 5 })).to eql(10.0)
462
563
  end
463
564
 
464
- it 'returns monthly fee for if just within limit limits' do
565
+ it 'returns monthly fee if just within limit limits' do
465
566
  expect(plan.monthly_fee_for({ bananas: 10, orders: 10, products: 10 })).to eql(10.0)
466
567
  end
467
568
 
@@ -475,40 +576,44 @@ describe 'FixedMeteredPlan' do
475
576
 
476
577
  end
477
578
 
478
- describe '#available_for?(counts)' do
479
579
 
480
- it 'raises if missing units for tier' do
481
- expect { plan.available_for?({ bananas: 5 }) }.to raise_error
580
+ describe '#amounts_for(counts)' do
581
+
582
+ it 'raises if missing unit for tier' do
583
+ expect { plan.amounts_for({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
482
584
  end
483
585
 
484
586
  it 'raises if missing unit for fixed limit' do
485
- expect { plan.available_for?({ products: 25 }) }.to raise_error
587
+ expect { plan.amounts_for({ products: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
486
588
  end
487
589
 
488
- it 'doent explode if negative key for limit with tier' do
489
- expect(plan.available_for?({ bananas: 5, orders: 5, products: -5 })).to eql(true)
590
+ it 'raises if over limits' do
591
+ expect { plan.amounts_for({ orders: 1000, products: 5 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
490
592
  end
491
593
 
492
- it 'returns true if below fixed limits' do
493
- expect(plan.available_for?({ bananas: 5, orders: 5, products: 5 })).to eql(true)
594
+ it 'doest raises if over limits but second arg is true' do
595
+ expect { plan.amounts_for({ orders: 1000, products: 5 }, true) }.not_to raise_error(Zillion::Plan::TierNotFoundError)
596
+ expect(plan.amounts_for({ orders: 1000, products: 5 }, true)).to eql({ orders: 990, base_fee: 10.0 })
494
597
  end
495
598
 
496
- it 'returns true if below fixed limits, even if no matching tier exists' do
497
- plan_opts[:tiers].delete(:products)
498
- expect(plan.available_for?({ bananas: 5, orders: 5, products: 5 })).to eql(true)
599
+ it 'doesnt explode if negative key for tiered count, but under fixed limit' do
600
+ expect(plan.amounts_for({ bananas: 5, orders: 5, products: -5 })).to eql({ base_fee: 10.0 })
499
601
  end
500
602
 
501
- it 'returns false if above fixed limit and no tier found' do
502
- plan_opts[:tiers].delete(:products)
503
- expect(plan.available_for?({ bananas: 5, orders: 5, products: 15 })).to eql(false)
603
+ it 'returns amounts for tier if below fixed limits' do
604
+ expect(plan.amounts_for({ bananas: 5, orders: 5, products: 5 })).to eql({ base_fee: 10.0 })
504
605
  end
505
606
 
506
- it 'returns true if above fixed limit but within tier limit' do
507
- expect(plan.available_for?({ bananas: 5, orders: 5, products: 15 })).to eql(true)
607
+ it 'returns amounts if just within limit limits' do
608
+ expect(plan.amounts_for({ bananas: 10, orders: 10, products: 10 })).to eql({ base_fee: 10.0 })
508
609
  end
509
610
 
510
- it 'returns true e if above fixed limit but within tier limit' do
511
- expect(plan.available_for?({ bananas: 5, orders: 15, products: 25 })).to eql(true)
611
+ it 'returns amounts for tier if within its limit' do
612
+ expect(plan.amounts_for({ bananas: 5, orders: 5, products: 25 })).to eql({:products=>30, :base_fee=>10.0})
613
+ end
614
+
615
+ it 'returns amounts for tier if within its limit' do
616
+ expect(plan.amounts_for({ bananas: 5, orders: 15, products: 25 })).to eql({:orders=>15, :products=>30, :base_fee=>10.0})
512
617
  end
513
618
 
514
619
  end
data/zillion.gemspec CHANGED
@@ -3,7 +3,7 @@
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = "zillion"
6
- s.version = '0.0.8'
6
+ s.version = '0.1.0'
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ['Tomás Pollak']
9
9
  s.email = ['tomas@forkhq.com']
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zillion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomás Pollak
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-05-18 00:00:00.000000000 Z
11
+ date: 2018-07-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -78,9 +78,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
78
  version: 1.3.6
79
79
  requirements: []
80
80
  rubyforge_project:
81
- rubygems_version: 2.2.0
81
+ rubygems_version: 2.6.13
82
82
  signing_key:
83
83
  specification_version: 4
84
84
  summary: A model for SaaS plans with fixed, tiered and metered pricing.
85
85
  test_files: []
86
- has_rdoc: