zillion 0.0.8 → 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 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: