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 +4 -4
- data/lib/zillion/plan.rb +38 -21
- data/spec/plans_spec.rb +211 -106
- data/zillion.gemspec +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91b941a168263933e9486bb2e3ba9dacf45b3d65
|
4
|
+
data.tar.gz: 6eafd20870092fa550a2f44e816d9776660c5e51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
#
|
138
|
-
tiers.
|
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
|
-
|
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
|
-
|
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
|
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
|
-
|
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 '#
|
42
|
+
describe '#available_for?(counts)' do
|
43
43
|
|
44
|
-
it 'returns
|
45
|
-
expect(plan.
|
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 '
|
48
|
+
it 'raises if not all units for limits are fed' do
|
49
49
|
other = FreePlan.new(limits: { bananas: 10 })
|
50
|
-
expect
|
50
|
+
expect { other.available_for?({ apples: 10 }) }.to raise_error(Zillion::Plan::MissingCountError)
|
51
51
|
end
|
52
52
|
|
53
|
-
it '
|
53
|
+
it 'returns false if limits/units match, but over the limit' do
|
54
54
|
other = FreePlan.new(limits: { bananas: 10 })
|
55
|
-
expect
|
55
|
+
expect(other.available_for?({ bananas: 20 })).to eql(false)
|
56
56
|
end
|
57
57
|
|
58
|
-
it '
|
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
|
60
|
+
expect(other.available_for?({ bananas: 5, apples: 2 })).to eql(true)
|
61
61
|
end
|
62
62
|
|
63
63
|
end
|
64
64
|
|
65
|
-
describe '#
|
65
|
+
describe '#monthly_fee_for(counts)' do
|
66
66
|
|
67
|
-
it 'returns
|
68
|
-
expect(plan.
|
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 '
|
71
|
+
it 'returns 0 if within limits' do
|
72
72
|
other = FreePlan.new(limits: { bananas: 10 })
|
73
|
-
expect
|
73
|
+
expect(other.monthly_fee_for({ bananas: 10 })).to eql(0)
|
74
74
|
end
|
75
75
|
|
76
|
-
it '
|
76
|
+
it 'raises if not within limits' do
|
77
77
|
other = FreePlan.new(limits: { bananas: 10 })
|
78
|
-
expect
|
78
|
+
expect { other.monthly_fee_for({ bananas: 11 }) }.to raise_error(Zillion::Plan::OverLimitsError)
|
79
79
|
end
|
80
80
|
|
81
|
-
it '
|
81
|
+
it 'raises if not all counts present' do
|
82
82
|
other = FreePlan.new(limits: { bananas: 10, apples: 10 })
|
83
|
-
expect
|
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 '#
|
107
|
+
describe '#available_for?(counts)' do
|
108
108
|
|
109
|
-
it 'returns
|
110
|
-
expect(plan.
|
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 '
|
113
|
+
it 'raises not all units for limits are fed' do
|
114
114
|
other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10 })
|
115
|
-
expect
|
115
|
+
expect { other.available_for?({ apples: 10 }) }.to raise_error
|
116
116
|
end
|
117
117
|
|
118
|
-
it '
|
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
|
120
|
+
expect(other.available_for?({ bananas: 20 })).to eql(false)
|
121
121
|
end
|
122
122
|
|
123
|
-
it '
|
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
|
125
|
+
expect(other.available_for?({ bananas: 5, apples: 2 })).to eql(true)
|
126
126
|
end
|
127
127
|
|
128
128
|
end
|
129
129
|
|
130
|
-
describe '#
|
130
|
+
describe '#monthly_fee_for(counts)' do
|
131
131
|
|
132
|
-
it 'returns
|
133
|
-
expect(plan.
|
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 '
|
136
|
+
it 'returns monthly_fee if within limits' do
|
137
137
|
other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10 })
|
138
|
-
expect
|
138
|
+
expect(other.monthly_fee_for({ bananas: 10 })).to eql(10.0)
|
139
139
|
end
|
140
140
|
|
141
|
-
it '
|
141
|
+
it 'raises if not within limits' do
|
142
142
|
other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10 })
|
143
|
-
expect
|
143
|
+
expect { other.monthly_fee_for({ bananas: 11 }) }.to raise_error(Zillion::Plan::OverLimitsError)
|
144
144
|
end
|
145
145
|
|
146
|
-
it '
|
146
|
+
it 'raises if not all counts present' do
|
147
147
|
other = FixedPlan.new(monthly_fee: 10, limits: { bananas: 10, apples: 10 })
|
148
|
-
expect
|
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 '#
|
177
|
+
describe '#available_for?(counts)' do
|
178
178
|
|
179
179
|
it 'raises if missing unit for tier' do
|
180
|
-
expect { plan.
|
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.
|
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.
|
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.
|
192
|
+
expect(plan.available_for?({ bananas: 5, products: 25 })).to eql(true)
|
193
193
|
end
|
194
194
|
|
195
195
|
end
|
196
196
|
|
197
|
-
describe '#
|
197
|
+
describe '#monthly_fee_for(counts)' do
|
198
198
|
|
199
199
|
it 'raises if missing unit for tier' do
|
200
|
-
expect { plan.
|
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.
|
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.
|
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.
|
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 '#
|
244
|
+
describe '#available_for?(counts)' do
|
245
245
|
|
246
246
|
it 'raises if missing unit for tier' do
|
247
|
-
expect { plan.
|
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.
|
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.
|
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
|
259
|
-
expect(plan.
|
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 '#
|
264
|
+
describe '#monthly_fee_for(counts)' do
|
265
265
|
|
266
266
|
it 'raises if missing unit for tier' do
|
267
|
-
expect { plan.
|
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.
|
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.
|
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
|
279
|
-
expect(plan.
|
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 '#
|
347
|
+
describe '#amounts_for(counts)' do
|
328
348
|
|
329
349
|
it 'raises if missing unit for tier' do
|
330
|
-
expect { plan.
|
350
|
+
expect { plan.amounts_for({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
|
331
351
|
end
|
332
352
|
|
333
|
-
it 'raises if
|
334
|
-
expect { plan.
|
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.
|
362
|
+
expect { plan.amounts_for({ products: 250 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
|
339
363
|
end
|
340
364
|
|
341
|
-
it '
|
342
|
-
expect
|
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
|
-
|
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
|
-
|
403
|
+
|
404
|
+
describe '#available_for?(counts)' do
|
376
405
|
|
377
406
|
it 'raises if missing unit for tier' do
|
378
|
-
expect { plan.
|
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.
|
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.
|
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 '#
|
448
|
+
describe '#amounts_for(counts)' do
|
396
449
|
|
397
450
|
it 'raises if missing unit for tier' do
|
398
|
-
expect { plan.
|
451
|
+
expect { plan.amounts_for({ bananas: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
|
399
452
|
end
|
400
453
|
|
401
|
-
it 'raises if
|
402
|
-
expect { plan.
|
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.
|
459
|
+
expect { plan.amounts_for({ products: 250, orders: 5, orders: 1 }) }.to raise_error(Zillion::Plan::TierNotFoundError)
|
407
460
|
end
|
408
461
|
|
409
|
-
it '
|
410
|
-
expect
|
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
|
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
|
-
|
481
|
-
|
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.
|
587
|
+
expect { plan.amounts_for({ products: 5 }) }.to raise_error(Zillion::Plan::MissingCountError)
|
486
588
|
end
|
487
589
|
|
488
|
-
it '
|
489
|
-
expect
|
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 '
|
493
|
-
expect
|
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 '
|
497
|
-
|
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
|
502
|
-
|
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
|
507
|
-
expect(plan.
|
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
|
511
|
-
expect(plan.
|
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
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
|
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:
|
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.
|
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:
|