shushu 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/helpers/http_helpers.rb +46 -0
- data/lib/models/account.rb +13 -0
- data/lib/models/acct_own.rb +31 -0
- data/lib/models/b_event.rb +31 -0
- data/lib/models/billable_unit.rb +35 -0
- data/lib/models/heart_beat.rb +10 -0
- data/lib/models/line_item.rb +19 -0
- data/lib/models/r_code.rb +14 -0
- data/lib/models/report.rb +51 -0
- data/lib/models/res_own.rb +31 -0
- data/lib/models/rev_rep.rb +13 -0
- data/lib/models/unit_group.rb +59 -0
- data/lib/presenters/base_presenter.rb +25 -0
- data/lib/presenters/line_item_presenter.rb +49 -0
- data/lib/presenters/report_presenter.rb +23 -0
- data/lib/presenters/unit_group_presenter.rb +63 -0
- data/lib/presenters/unit_presenter.rb +34 -0
- data/lib/services/line_item_builder.rb +17 -0
- data/lib/shushu.rb +64 -0
- data/readme.md +252 -0
- data/test/fixtures.rb +29 -0
- data/test/models/b_event_test.rb +20 -0
- data/test/models/billable_unit_test.rb +8 -0
- data/test/models/line_item_test.rb +10 -0
- data/test/models/r_code_test.rb +18 -0
- data/test/models/rev_rep_test.rb +12 -0
- data/test/models/unit_group_test.rb +17 -0
- data/test/presenters/line_item_presenter_test.rb +36 -0
- data/test/presenters/report_presenter_test.rb +22 -0
- data/test/services/line_item_builder_test.rb +30 -0
- data/test/test_helper.rb +19 -0
- metadata +117 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
module Shushu
|
2
|
+
module HttpHelpers
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def build_q_params(hash)
|
6
|
+
"?" + hash.map do |k, v|
|
7
|
+
CGI.escape("#{k}=#{v}")
|
8
|
+
end.join("&")
|
9
|
+
end
|
10
|
+
|
11
|
+
def headers
|
12
|
+
{:content_type => :json, :accept => :json}
|
13
|
+
end
|
14
|
+
|
15
|
+
def enc_json(hash)
|
16
|
+
Yajl::Encoder.encode(hash)
|
17
|
+
end
|
18
|
+
|
19
|
+
def dec_json(json)
|
20
|
+
Yajl::Parser.parse(json)
|
21
|
+
end
|
22
|
+
|
23
|
+
def handle_req(&blk)
|
24
|
+
begin
|
25
|
+
resp = yield
|
26
|
+
dec_json(resp.body)
|
27
|
+
rescue RestClient::Exception => e
|
28
|
+
body = dec_json(e.http_body)
|
29
|
+
case e.http_code
|
30
|
+
when 404
|
31
|
+
when 401
|
32
|
+
raise(AuthorizationError, "Response: #{body.inspect}")
|
33
|
+
when 403
|
34
|
+
when 409
|
35
|
+
when 500
|
36
|
+
raise(UnexpectedError, "Response: #{body.inspect}")
|
37
|
+
when 503
|
38
|
+
raise(ServiceDownError, "#{Shushu.url} is down for maintenance.")
|
39
|
+
else
|
40
|
+
raise(UnexpectedError, "Response: #{body.inspect}")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Shushu
|
2
|
+
module AcctOwn
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def act(args)
|
6
|
+
pmid, eid = args.delete(:payment_method_id), args.delete(:entity_id)
|
7
|
+
Shushu.handle_req {RestClient.post(acct_own_url(pmid, eid), args, Shushu.headers)}
|
8
|
+
end
|
9
|
+
|
10
|
+
def xfr(args)
|
11
|
+
prev_pmid, prev_eid = args.delete(:prev_payment_method_id), args.delete(:prev_entity_id)
|
12
|
+
Shushu.handle_req {RestClient.put(acct_own_url(prev_pmid, prev_eid), args, Shushu.headers)}
|
13
|
+
end
|
14
|
+
|
15
|
+
def deact(args)
|
16
|
+
pmid = args.delete(:payment_method_id)
|
17
|
+
eid = args.delete(:entity_id)
|
18
|
+
account_id = args[:account_id]
|
19
|
+
time = args[:time]
|
20
|
+
Shushu.handle_req {RestClient.delete([acct_own_url(pmid, eid), CGI.encode("?account_id=#{account_id}&time=#{time}")].join)}
|
21
|
+
end
|
22
|
+
|
23
|
+
def query(args)
|
24
|
+
Shushu.handle_req {RestClient.get([Shushu.url, "/accounts/#{args[:account_id]}/resource_ownerships"].join)}
|
25
|
+
end
|
26
|
+
|
27
|
+
def acct_own_url(payment_method_id, entity_id)
|
28
|
+
[Shushu.url, "/payment_methods/#{payment_method_id}/account_ownerships/#{entity_id}"].join
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Shushu
|
2
|
+
module BEvent
|
3
|
+
extend self
|
4
|
+
|
5
|
+
OPEN = "open"
|
6
|
+
CLOSE = "close"
|
7
|
+
|
8
|
+
def all
|
9
|
+
Shushu.handle_req {RestClient.get([Shushu.url, "/billable_events"].join, Shushu.headers)}
|
10
|
+
end
|
11
|
+
|
12
|
+
def open(args)
|
13
|
+
hid = args.delete(:hid)
|
14
|
+
entity_id = args.delete(:entity_id)
|
15
|
+
args[:state] = OPEN
|
16
|
+
Shushu.handle_req {RestClient.put(events_url(hid, entity_id), args, Shushu.headers)}
|
17
|
+
end
|
18
|
+
|
19
|
+
def close(args)
|
20
|
+
hid = args.delete(:hid)
|
21
|
+
entity_id = args.delete(:entity_id)
|
22
|
+
args[:state] = CLOSE
|
23
|
+
Shushu.handle_req {RestClient.put(events_url(hid, entity_id), args, Shushu.headers)}
|
24
|
+
end
|
25
|
+
|
26
|
+
def events_url(hid, entity_id)
|
27
|
+
[Shushu.url, "/resources/#{hid}/billable_events/#{entity_id}"].join
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Shushu
|
2
|
+
class BillableUnit
|
3
|
+
attr_accessor(
|
4
|
+
:hid,
|
5
|
+
:from,
|
6
|
+
:to,
|
7
|
+
:qty,
|
8
|
+
:rate,
|
9
|
+
:rate_period,
|
10
|
+
:product_group,
|
11
|
+
:product_name
|
12
|
+
)
|
13
|
+
|
14
|
+
def initialize(args_hash)
|
15
|
+
@hid = args_hash["hid"]
|
16
|
+
@from = args_hash["from"]
|
17
|
+
@to = args_hash["to"]
|
18
|
+
@qty = args_hash["qty"].to_f
|
19
|
+
@rate = args_hash["rate"].to_i
|
20
|
+
@rate_period = args_hash["rate_period"]
|
21
|
+
@product_name = args_hash["product_name"]
|
22
|
+
@product_group = args_hash["product_group"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def id
|
26
|
+
#TODO remove stub
|
27
|
+
Time.now.to_i
|
28
|
+
end
|
29
|
+
|
30
|
+
def total
|
31
|
+
@rate * @qty
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Shushu
|
2
|
+
class LineItem
|
3
|
+
|
4
|
+
attr_reader :unit_groups
|
5
|
+
|
6
|
+
def initialize(unit_groups)
|
7
|
+
@unit_groups = unit_groups
|
8
|
+
end
|
9
|
+
|
10
|
+
def app_name
|
11
|
+
@unit_groups.first.hid
|
12
|
+
end
|
13
|
+
|
14
|
+
def total
|
15
|
+
@unit_groups.map(&:total).reduce(:+)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Shushu
|
2
|
+
class Report
|
3
|
+
attr_accessor(
|
4
|
+
:from,
|
5
|
+
:to,
|
6
|
+
:billable_units
|
7
|
+
)
|
8
|
+
|
9
|
+
def fetch
|
10
|
+
Shushu.handle_req do
|
11
|
+
RestClient.get([Shushu.url, resource].join, {:params => {:from => from.utc.to_s, :to => to.utc.to_s}})
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def billable_units
|
16
|
+
@billable_units ||= report["billable_units"].map {|buh| BillableUnit.new(buh)}
|
17
|
+
end
|
18
|
+
|
19
|
+
def total
|
20
|
+
report["total"]
|
21
|
+
end
|
22
|
+
|
23
|
+
def report
|
24
|
+
@report ||= fetch
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class UsageReport < Report
|
29
|
+
attr_accessor :account_id
|
30
|
+
|
31
|
+
def initialize(account_id, from, to)
|
32
|
+
@account_id, @from, @to = account_id, from, to
|
33
|
+
end
|
34
|
+
|
35
|
+
def resource
|
36
|
+
"/accounts/#{@account_id}/usage_reports"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Invoice < Report
|
41
|
+
attr_accessor :payment_method_id
|
42
|
+
|
43
|
+
def initialize(payment_method_id, from, to)
|
44
|
+
@payment_method_id, @from, @to = payment_method_id, from, to
|
45
|
+
end
|
46
|
+
|
47
|
+
def resource
|
48
|
+
"/payment_methods/#{payment_method_id}/invoices"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Shushu
|
2
|
+
module ResOwn
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def act(args)
|
6
|
+
aid, eid = args.delete(:account_id), args.delete(:entity_id)
|
7
|
+
Shushu.handle_req {RestClient.post(res_own_url(aid, eid), args, Shushu.headers)}
|
8
|
+
end
|
9
|
+
|
10
|
+
def xfr(args)
|
11
|
+
prev_aid, prev_eid = args.delete(:prev_account_id), args.delete(:prev_entity_id)
|
12
|
+
Shushu.handle_req {RestClient.put(res_own_url(prev_aid, prev_eid), args, Shushu.headers)}
|
13
|
+
end
|
14
|
+
|
15
|
+
def deact(args)
|
16
|
+
aid = args.delete(:account_id)
|
17
|
+
eid = args.delete(:entity_id)
|
18
|
+
hid = args[:hid]
|
19
|
+
time = args[:time]
|
20
|
+
Shushu.handle_req {RestClient.delete([res_own_url(aid, eid), CGI.encode("?hid=#{hid}&time=#{time}")].join)}
|
21
|
+
end
|
22
|
+
|
23
|
+
def query(args)
|
24
|
+
Shushu.handle_req {RestClient.get([Shushu.url, "/accounts/#{args[:account_id]}/resource_ownerships"].join)}
|
25
|
+
end
|
26
|
+
|
27
|
+
def res_own_url(account_id, entity_id)
|
28
|
+
[Shushu.url, "/accounts/#{account_id}/resource_ownerships/#{entity_id}"].join
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Shushu
|
2
|
+
class UnitGroup
|
3
|
+
InvalidUnitGroup = Class.new(ShushuException)
|
4
|
+
|
5
|
+
attr_reader :units
|
6
|
+
|
7
|
+
def initialize(units)
|
8
|
+
@units = units
|
9
|
+
check_rates
|
10
|
+
end
|
11
|
+
|
12
|
+
def check_rates
|
13
|
+
if @units.map(&:rate).uniq.length > 1
|
14
|
+
raise(InvalidUnitGroup, "Rates must be homogenius.")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def total
|
19
|
+
rate * qty
|
20
|
+
end
|
21
|
+
|
22
|
+
def qty
|
23
|
+
@units.map(&:qty).reduce(:+)
|
24
|
+
end
|
25
|
+
|
26
|
+
def rate
|
27
|
+
sample_unit.rate
|
28
|
+
end
|
29
|
+
|
30
|
+
def rate_period
|
31
|
+
sample_unit.rate_period
|
32
|
+
end
|
33
|
+
|
34
|
+
def name
|
35
|
+
sample_unit.product_name
|
36
|
+
end
|
37
|
+
|
38
|
+
def hid
|
39
|
+
sample_unit.hid
|
40
|
+
end
|
41
|
+
|
42
|
+
def product_name
|
43
|
+
sample_unit.product_name
|
44
|
+
end
|
45
|
+
|
46
|
+
def product_group
|
47
|
+
sample_unit.product_group
|
48
|
+
end
|
49
|
+
|
50
|
+
def description
|
51
|
+
sample_unit.product_name
|
52
|
+
end
|
53
|
+
|
54
|
+
def sample_unit
|
55
|
+
@sample_unit ||= @units.sample
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Shushu
|
2
|
+
class BasePresenter
|
3
|
+
|
4
|
+
def money(cents)
|
5
|
+
add_commas(pennies_to_dollar(cents.to_i))
|
6
|
+
end
|
7
|
+
|
8
|
+
def add_commas(str)
|
9
|
+
str.reverse.scan(/(?:\d*\.)?\d{1,3}-?/).join(',').reverse
|
10
|
+
end
|
11
|
+
|
12
|
+
def pennies_to_dollar(qty)
|
13
|
+
sprintf("%.2f", qty / 100.0)
|
14
|
+
end
|
15
|
+
|
16
|
+
def trunc_hours(hrs)
|
17
|
+
sprintf("%.3f", hrs)
|
18
|
+
end
|
19
|
+
|
20
|
+
def date_range(s, f)
|
21
|
+
s.strftime('%d %b %H:%M ') + " - " + f.strftime('%d %b %H:%M ')
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Shushu
|
2
|
+
class LineItemPresenter < BasePresenter
|
3
|
+
|
4
|
+
def initialize(line_item)
|
5
|
+
@line_item = line_item
|
6
|
+
@unit_groups = @line_item.unit_groups
|
7
|
+
@unit_group_presenters = @unit_groups.map {|ug| UnitGroupPresenter.new(ug)}
|
8
|
+
end
|
9
|
+
|
10
|
+
def total
|
11
|
+
money(@line_item.total)
|
12
|
+
end
|
13
|
+
|
14
|
+
def name
|
15
|
+
@line_item.app_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def unit_group_rate(product_group)
|
19
|
+
unit_group_presenters(product_group).sample.rate
|
20
|
+
end
|
21
|
+
|
22
|
+
def unit_group_total(product_group)
|
23
|
+
money(
|
24
|
+
unit_group_presenters(product_group).
|
25
|
+
map(&:unit_group).
|
26
|
+
map(&:total).
|
27
|
+
reduce(:+)
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def unit_group_qty(product_group)
|
32
|
+
trunc_hours(
|
33
|
+
unit_group_presenters(product_group).
|
34
|
+
map(&:unit_group).
|
35
|
+
map(&:qty).
|
36
|
+
reduce(:+)
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def unit_group_presenters(product_group=nil)
|
41
|
+
if product_group
|
42
|
+
@unit_group_presenters.select {|ugp| ugp.product_group == product_group}
|
43
|
+
else
|
44
|
+
@unit_group_presenters
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Shushu
|
2
|
+
class ReportPresenter < BasePresenter
|
3
|
+
|
4
|
+
def initialize(report, line_item_builder=LineItemBuilder)
|
5
|
+
@report = report
|
6
|
+
@units = @report.billable_units
|
7
|
+
@builder = line_item_builder
|
8
|
+
end
|
9
|
+
|
10
|
+
def total
|
11
|
+
money(@report.total)
|
12
|
+
end
|
13
|
+
|
14
|
+
def line_item_presenters
|
15
|
+
@line_item_presenters ||= begin
|
16
|
+
@builder.build(@units).map do |li|
|
17
|
+
LineItemPresenter.new(li)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module Shushu
|
2
|
+
class UnitGroupPresenter < BasePresenter
|
3
|
+
|
4
|
+
attr_reader :unit_group
|
5
|
+
|
6
|
+
def initialize(unit_group)
|
7
|
+
@unit_group = unit_group
|
8
|
+
end
|
9
|
+
|
10
|
+
def id
|
11
|
+
[:hid, :name].map {|m| unit_group.send(m)}.join("_").gsub("-", "_")
|
12
|
+
end
|
13
|
+
|
14
|
+
def unit_presenters
|
15
|
+
@unit_presenters ||= @unit_group.units.map {|u| UnitPresenter.new(u)}
|
16
|
+
end
|
17
|
+
|
18
|
+
def daily_report_url
|
19
|
+
#TODO remove chart stub
|
20
|
+
#payments_uri("/billable_units/daily")
|
21
|
+
"/billable_units/daily"
|
22
|
+
end
|
23
|
+
|
24
|
+
def compacted_reports
|
25
|
+
#TODO remove chart stub
|
26
|
+
#BillableUnitReporter.compacted(@unit_group.units)
|
27
|
+
[]
|
28
|
+
end
|
29
|
+
|
30
|
+
def total
|
31
|
+
money(@unit_group.total)
|
32
|
+
end
|
33
|
+
|
34
|
+
def qty
|
35
|
+
trunc_hours(@unit_group.qty)
|
36
|
+
end
|
37
|
+
|
38
|
+
def rate
|
39
|
+
@unit_group.rate
|
40
|
+
end
|
41
|
+
|
42
|
+
def name
|
43
|
+
@unit_group.name
|
44
|
+
end
|
45
|
+
|
46
|
+
def description
|
47
|
+
@unit_group.description
|
48
|
+
end
|
49
|
+
|
50
|
+
def product_name
|
51
|
+
@unit_group.product_name
|
52
|
+
end
|
53
|
+
|
54
|
+
def product_group
|
55
|
+
@unit_group.product_group
|
56
|
+
end
|
57
|
+
|
58
|
+
def unit_of_measure
|
59
|
+
@unit_group.rate_period
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Shushu
|
2
|
+
class UnitPresenter < BasePresenter
|
3
|
+
|
4
|
+
def initialize(unit)
|
5
|
+
@unit = unit
|
6
|
+
end
|
7
|
+
|
8
|
+
def total
|
9
|
+
money(@unit.total)
|
10
|
+
end
|
11
|
+
|
12
|
+
def rate
|
13
|
+
[money(@unit.rate), @unit.rate_period].join("/")
|
14
|
+
end
|
15
|
+
|
16
|
+
def qty
|
17
|
+
trunc_hours(@unit.qty)
|
18
|
+
end
|
19
|
+
|
20
|
+
def start
|
21
|
+
@unit.from
|
22
|
+
end
|
23
|
+
|
24
|
+
def end
|
25
|
+
@unit.to
|
26
|
+
end
|
27
|
+
|
28
|
+
def description
|
29
|
+
@unit.product_name
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Shushu
|
2
|
+
module LineItemBuilder
|
3
|
+
extend self
|
4
|
+
|
5
|
+
# Give build() a collection of units and it will return a
|
6
|
+
# collection of line_items. Each line_item will have a collection
|
7
|
+
# of unit_groups wich will hold a collection of units
|
8
|
+
|
9
|
+
def build(units)
|
10
|
+
units.group_by(&:hid).map do |hid, units_by_hid|
|
11
|
+
units_by_hid.group_by(&:product_name).map do |product_name, units_by_name|
|
12
|
+
UnitGroup.new(units_by_name)
|
13
|
+
end
|
14
|
+
end.map {|unit_groups| LineItem.new(unit_groups)}.flatten
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/shushu.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "yajl"
|
2
|
+
require "rest-client"
|
3
|
+
require "helpers/http_helpers"
|
4
|
+
|
5
|
+
require "active_support/inflector"
|
6
|
+
Inflector = ActiveSupport::Inflector.send(:extend, ActiveSupport::Inflector)
|
7
|
+
|
8
|
+
module Shushu
|
9
|
+
extend self
|
10
|
+
extend HttpHelpers
|
11
|
+
|
12
|
+
ShushuException = Class.new(Exception)
|
13
|
+
AuthenticationError = Class.new(ShushuException)
|
14
|
+
AuthorizationError = Class.new(ShushuException)
|
15
|
+
ServiceDownError = Class.new(ShushuException)
|
16
|
+
UnexpectedError = Class.new(ShushuException)
|
17
|
+
SymanticError = Class.new(ShushuException)
|
18
|
+
|
19
|
+
def url
|
20
|
+
Client.url
|
21
|
+
end
|
22
|
+
|
23
|
+
def url=(url)
|
24
|
+
Client.url = url
|
25
|
+
end
|
26
|
+
|
27
|
+
class Client
|
28
|
+
def self.url=(url)
|
29
|
+
@@url = url
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.url
|
33
|
+
@@url || ENV["SHUSHU_URL"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(url)
|
37
|
+
self.class.url = url
|
38
|
+
end
|
39
|
+
|
40
|
+
def [](api)
|
41
|
+
Shushu.const_get(Inflector.camelize(api))
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
require "models/account"
|
48
|
+
require "models/billable_unit"
|
49
|
+
require "models/report"
|
50
|
+
require "models/unit_group"
|
51
|
+
require "models/line_item"
|
52
|
+
require "models/b_event"
|
53
|
+
require "models/res_own"
|
54
|
+
require "models/r_code"
|
55
|
+
require "models/rev_rep"
|
56
|
+
require "models/heart_beat"
|
57
|
+
|
58
|
+
require "services/line_item_builder"
|
59
|
+
|
60
|
+
require "presenters/base_presenter"
|
61
|
+
require "presenters/unit_group_presenter"
|
62
|
+
require "presenters/unit_presenter"
|
63
|
+
require "presenters/line_item_presenter"
|
64
|
+
require "presenters/report_presenter"
|
data/readme.md
ADDED
@@ -0,0 +1,252 @@
|
|
1
|
+
# Shushu Client
|
2
|
+
|
3
|
+
## Purpose
|
4
|
+
|
5
|
+
This gem wraps the APIs defined [here](https://github.com/heroku/shushu/tree/master/doc).
|
6
|
+
The shushu client also provides a set of objects that help with the presentation
|
7
|
+
of an invoice.
|
8
|
+
|
9
|
+
## Setup
|
10
|
+
|
11
|
+
```bash
|
12
|
+
$ gem install shushu
|
13
|
+
```
|
14
|
+
|
15
|
+
```bash
|
16
|
+
# Optional. When set, shushu will ignore bad responses from Shushu's API.
|
17
|
+
# Default: false
|
18
|
+
$SHUSHU_CLIENT_UNSAFE=true
|
19
|
+
|
20
|
+
# Required
|
21
|
+
# Default: nil
|
22
|
+
$SHUSHU_URL=https://shushu-stg.heroku.com
|
23
|
+
```
|
24
|
+
|
25
|
+
**Ruby configuration will take precedence over environment variables.**
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
Shushu.url = "https://core:secret@shushu-stg.heroku.com"
|
29
|
+
```
|
30
|
+
|
31
|
+
## Usage
|
32
|
+
|
33
|
+
### PaymentMethods
|
34
|
+
|
35
|
+
This API deals primarily with credit cards. PaymentMethods can be created
|
36
|
+
indipendintly of Accounts. You will need a payment_method to generate an
|
37
|
+
invoice.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
#TODO Build API
|
41
|
+
```
|
42
|
+
|
43
|
+
### Accounts
|
44
|
+
|
45
|
+
This API deals with accounts which is a primitive for grouping resources. You
|
46
|
+
will need an account to generate a usage report.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
Shushu::Account.create
|
50
|
+
#=> {:id => "001"}
|
51
|
+
```
|
52
|
+
|
53
|
+
### AccountOwnerships
|
54
|
+
|
55
|
+
Use this API when you want to setup associations between Vault accounts and
|
56
|
+
Vault payment_methods.
|
57
|
+
|
58
|
+
For complete details on the semantics of this API, read the [AccountOwnerships
|
59
|
+
API docs.](https://github.com/heroku/shushu/blob/master/doc/account_ownership_api.md)
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
# To associate an account with a payment_method
|
63
|
+
Shushu::AcctOwn.act(
|
64
|
+
:account_id => vault_account_id,
|
65
|
+
:payment_method_id => payment_method_id,
|
66
|
+
:entity_id => entity_id,
|
67
|
+
:time => time
|
68
|
+
)
|
69
|
+
#=> {"payment_method_id"=>"123", "account_id"=>"1", "entity_id"=>"entity123"}
|
70
|
+
|
71
|
+
# Now we need to change the payment_method on an account
|
72
|
+
Shushu::AcctOwn.xfr(
|
73
|
+
:prev_payment_method_id => prev_payment_method_id,
|
74
|
+
:payment_method_id => new_payment_method_id,
|
75
|
+
:account_id => account_id,
|
76
|
+
:prev_entity_id => prev_entity_id,
|
77
|
+
:entity_id => entity_id,
|
78
|
+
:time => time
|
79
|
+
)
|
80
|
+
#=> {"payment_method_id"=>"456", "account_id"=>"1", "entity_id"=>"event124"}
|
81
|
+
```
|
82
|
+
|
83
|
+
### RateCodes
|
84
|
+
|
85
|
+
Before you can send BillableEvents to Shushu, you will need to provision a
|
86
|
+
RateCode.
|
87
|
+
|
88
|
+
* rate - int. units should be pennies.
|
89
|
+
* product_group - string.
|
90
|
+
* product_name - string. optional. you can also pass the product name in an event.
|
91
|
+
|
92
|
+
[Shushu API Docs](https://github.com/heroku/shushu/blob/master/doc/rate_code_api.md)
|
93
|
+
|
94
|
+
```ruby
|
95
|
+
Shushu::RCode.create(
|
96
|
+
:rate => 5,
|
97
|
+
:product_group => "addon",
|
98
|
+
:product_name => "postgres"
|
99
|
+
)
|
100
|
+
#=> {:slug => "AO01", :rate => 5, :product_group => "addon", :product_name => "postgres"}
|
101
|
+
```
|
102
|
+
|
103
|
+
### ResourceOwnerships
|
104
|
+
|
105
|
+
Use this API when dealing with resources and Vault accounts. For instance:
|
106
|
+
Heroku apps and Teams.
|
107
|
+
|
108
|
+
For complete details on the semantics of this API, read the [ResourceOwnerships
|
109
|
+
API docs.](https://github.com/heroku/shushu/blob/master/doc/resource_ownership_api.md)
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
# When a new app is created, activate a new resource_ownership record.
|
113
|
+
Shushu::ResOwn.act(
|
114
|
+
:hid => hid,
|
115
|
+
:entity_id => entity_id,
|
116
|
+
:account_id => vault_account_id,
|
117
|
+
:time => time
|
118
|
+
)
|
119
|
+
#=> {"hid"=>"123", "account_id"=>"1", "entity_id"=>"event123"}
|
120
|
+
|
121
|
+
|
122
|
+
# When an app is transfered to another vault account, transfer the resource_ownership record.
|
123
|
+
Shushu::ResOwn.xfr(
|
124
|
+
:hid => hid,
|
125
|
+
:prev_entity_id => prev_entity_id,
|
126
|
+
:prev_vault_account_id => prev_vault_account_id,
|
127
|
+
:entity_id => entity_id,
|
128
|
+
:account_id => new_vault_account_id,
|
129
|
+
:time => time
|
130
|
+
)
|
131
|
+
#=> {"hid"=>"123", "account_id"=>"1", "entity_id"=>"event123"}
|
132
|
+
|
133
|
+
# When an app is destroyed, deactivate the resource_ownership record.
|
134
|
+
Shushu::ResOwn.deact(
|
135
|
+
:hid => hid,
|
136
|
+
:entity_id => entity_id,
|
137
|
+
:account_id => vault_account_id,
|
138
|
+
:time => time
|
139
|
+
)
|
140
|
+
#=> {"hid"=>"123", "account_id"=>"1", "entity_id"=>"event123"}
|
141
|
+
```
|
142
|
+
|
143
|
+
### BillableEvents
|
144
|
+
|
145
|
+
Use this API when you want to start billing for a resource. You can start
|
146
|
+
emitting events prior to setting up relationships between accounts and
|
147
|
+
payment_methods. (Although, usage reports and invoices will not be available
|
148
|
+
until account_ownerships and resource_ownerships have been established.)
|
149
|
+
|
150
|
+
For complete details on the semantics of this API, read the [BillableEvents
|
151
|
+
API docs.](https://github.com/heroku/shushu/blob/master/doc/events_api.md)
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
# Open an event when you would like to start billing.
|
155
|
+
Shushu::BEvent.open(
|
156
|
+
:entity_id => entity_id,
|
157
|
+
:hid => hid,
|
158
|
+
:time => time,
|
159
|
+
:rate_code => rate_code,
|
160
|
+
:product_name => product_name,
|
161
|
+
:qty => qty
|
162
|
+
)
|
163
|
+
|
164
|
+
# Don't forget to close it.
|
165
|
+
Shushu::BEvent.close(
|
166
|
+
:entity_id => entity_id,
|
167
|
+
:time => time
|
168
|
+
)
|
169
|
+
```
|
170
|
+
|
171
|
+
### UsageReports
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
report = Shushu::UsageReport.new(account_id, from, to)
|
175
|
+
report.billable_units
|
176
|
+
```
|
177
|
+
|
178
|
+
|
179
|
+
### Invoices
|
180
|
+
|
181
|
+
```ruby
|
182
|
+
invoice = Shushu::UsageReport.new(account_id, from, to)
|
183
|
+
invoice.billable_units
|
184
|
+
```
|
185
|
+
|
186
|
+
|
187
|
+
### Report Generation
|
188
|
+
|
189
|
+
Invoices and UsageReports can be used for report generation. Basically, the
|
190
|
+
report generation code expects a collection of BillableUnits. BillableUnits are
|
191
|
+
returned from both the Invoice API and the UsageReport API. However, the details
|
192
|
+
of the billable_units may be different with respect to the type of the report.
|
193
|
+
|
194
|
+
#### Presenters
|
195
|
+
|
196
|
+
Clients of this library will want to generate some sort of view for the reports, the
|
197
|
+
presenter objects were created to aid with that effort. You should only need to
|
198
|
+
use the presenters while building views. Each view wraps a simple model object.
|
199
|
+
All of the models and presenters are derived from the billable_unit which is
|
200
|
+
retreived from the remote API.
|
201
|
+
|
202
|
+
Report --> LineItem --> UnitGroup --> Billable Unit
|
203
|
+
|
204
|
+
|
205
|
+
#### ReportPresenter
|
206
|
+
|
207
|
+
The report presenter is how you will kick off the process of generating a
|
208
|
+
report. You hand it a report object, either a UsageReport or and Invoice, and
|
209
|
+
using the reports billable_units, it will build the line_items and a set of
|
210
|
+
line_item_presenters for the line_items.
|
211
|
+
|
212
|
+
#### LineItemBuilder
|
213
|
+
|
214
|
+
The ReportPresenter will create a set of line_items based upon the
|
215
|
+
billable_units in the report. The default is to group things by HID. Thus there
|
216
|
+
will be a line_item for each distinct HID in the set of billable_units. The
|
217
|
+
builder will also give a collection of unit_groups to the line_item. By default,
|
218
|
+
the unit_groups will be partitioned by the product_group. (i.e. dyno, addon)
|
219
|
+
|
220
|
+
You can customize the LineItemBuilder by creating a new class that responds to
|
221
|
+
build and passing it to the ReportPresenter.
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
ReportPresenter.new(report, CustomLineItemBuilder)
|
225
|
+
```
|
226
|
+
|
227
|
+
#### LineItemPresenter
|
228
|
+
|
229
|
+
The LineItemPresenter is responsible for handling the total and names of the
|
230
|
+
line_items. It also manages the set of unit_groups. Since the default
|
231
|
+
LineItemBuilder partitioned unit_groups based upon product_group, you can ask
|
232
|
+
the LineItemPresenter for infomation about subsets of unit_groups. For instance:
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
line_item_presenter.unit_group_presenters("dyno")
|
236
|
+
line_item_presenter.unit_group_total("dyno")
|
237
|
+
line_item_presenter.unit_group_qty("dyno")
|
238
|
+
```
|
239
|
+
|
240
|
+
|
241
|
+
#### UnitGroupPresenter
|
242
|
+
|
243
|
+
UnitGroups are collections of billable_units that are partitioned by
|
244
|
+
product_name. So if there exists a set of billable_units that all have
|
245
|
+
product_group = "dyno" and product_name= "worker", then this presenter
|
246
|
+
will give you information about that group.
|
247
|
+
|
248
|
+
|
249
|
+
#### UnitPresenter
|
250
|
+
|
251
|
+
Finally, the UnitPresenter wraps a billable_unit and exposes methods to show
|
252
|
+
totals, quantities and other meta-data.
|
data/test/fixtures.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module Fixtures
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def billable_units
|
5
|
+
[
|
6
|
+
{
|
7
|
+
:hid => 123,
|
8
|
+
:from => Time.mktime(2000,1),
|
9
|
+
:to => Time.mktime(2000,2),
|
10
|
+
:qty => 744,
|
11
|
+
:rate => 5,
|
12
|
+
:rate_period => "hour",
|
13
|
+
:product_group => "dyno",
|
14
|
+
:product_name => "web"
|
15
|
+
},
|
16
|
+
{
|
17
|
+
:hid => 123,
|
18
|
+
:from => Time.mktime(2000,1),
|
19
|
+
:to => Time.mktime(2000,2),
|
20
|
+
:qty => 744,
|
21
|
+
:rate => 5,
|
22
|
+
:rate_period => "hour",
|
23
|
+
:product_group => "dyno",
|
24
|
+
:product_name => "worker"
|
25
|
+
}
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class BEventTest < ShushuTest
|
4
|
+
def test_open
|
5
|
+
our_params = {
|
6
|
+
:hid => "app123",
|
7
|
+
:entity_id => 1,
|
8
|
+
:rate_code => "RT01",
|
9
|
+
:time => Time.utc(2012,1).to_s,
|
10
|
+
:product_name => "web",
|
11
|
+
:qty => 1
|
12
|
+
}
|
13
|
+
api_resp_body = Shushu::HttpHelpers.enc_json(our_params)
|
14
|
+
Shushu.url = "http://provider:password@shushu.heroku.com"
|
15
|
+
FakeWeb.register_uri(:put, (Shushu.url + "/resources/app123/billable_events/1"), :body => api_resp_body)
|
16
|
+
event = Shushu::BEvent.open(our_params)
|
17
|
+
assert_equal(1, event["entity_id"])
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class LineItemTest < ShushuTest
|
4
|
+
def test_total
|
5
|
+
unit1 = Shushu::BillableUnit.new("rate" => 10, "qty" => 10)
|
6
|
+
unit_group1 = Shushu::UnitGroup.new([unit1])
|
7
|
+
line_item = Shushu::LineItem.new([unit_group1])
|
8
|
+
assert_equal(100, line_item.total)
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class RCodeTest < ShushuTest
|
4
|
+
def test_open
|
5
|
+
our_params = {
|
6
|
+
:rate => 5,
|
7
|
+
:product_group => "addon",
|
8
|
+
:product_name => "postgres",
|
9
|
+
:description => "A database service."
|
10
|
+
}
|
11
|
+
api_resp_body = Shushu::HttpHelpers.enc_json(our_params.merge(:slug => "AO01"))
|
12
|
+
Shushu.url = "http://provider:password@shushu.heroku.com"
|
13
|
+
FakeWeb.register_uri(:post, (Shushu.url + "/rate_codes"), :body => api_resp_body)
|
14
|
+
rate_code = Shushu::RCode.create(our_params)
|
15
|
+
assert_equal("AO01", rate_code["slug"])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class RevRepTest < ShushuTest
|
4
|
+
def test_for_month
|
5
|
+
api_resp_body = Shushu::HttpHelpers.enc_json({:ok => true})
|
6
|
+
Shushu.url = "http://provider:password@shushu.heroku.com"
|
7
|
+
url = Shushu.url + "/rev_rep?from%3D2011-01-01+00%3A00%3A00+UTC&to%3D2011-02-01+00%3A00%3A00+UTC"
|
8
|
+
FakeWeb.register_uri(:get, url, :body => api_resp_body)
|
9
|
+
Shushu::RevRep.get(Time.utc(2011,1), Time.utc(2011,2))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class UnitGroupTest < ShushuTest
|
4
|
+
def test_total
|
5
|
+
unit1 = Shushu::BillableUnit.new("rate" => 10, "qty" => 10)
|
6
|
+
unit_group1 = Shushu::UnitGroup.new([unit1])
|
7
|
+
assert_equal(100, unit_group1.total)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_check_rates
|
11
|
+
unit1 = Shushu::BillableUnit.new("rate" => 10, "qty" => 10)
|
12
|
+
unit2 = Shushu::BillableUnit.new("rate" => 9, "qty" => 10)
|
13
|
+
unit_group1 = Shushu::UnitGroup.new([unit1])
|
14
|
+
unit_group2 = Shushu::UnitGroup.new([unit2])
|
15
|
+
assert_raises(Shushu::UnitGroup::InvalidUnitGroup) {Shushu::UnitGroup.new([unit1, unit2])}
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class LineItemPresenterTest < ShushuTest
|
4
|
+
def test_unit_group_total
|
5
|
+
unit1 = Shushu::BillableUnit.new("rate" => 1000, "qty" => 10, "product_group" => "dyno")
|
6
|
+
unit_group1 = Shushu::UnitGroup.new([unit1])
|
7
|
+
line_item = Shushu::LineItem.new([unit_group1])
|
8
|
+
lip = Shushu::LineItemPresenter.new(line_item)
|
9
|
+
assert_equal("100.00", lip.unit_group_total("dyno"))
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_unit_group_qty
|
13
|
+
unit1 = Shushu::BillableUnit.new("rate" => 1000, "qty" => 10, "product_group" => "dyno")
|
14
|
+
unit2 = Shushu::BillableUnit.new("rate" => 1000, "qty" => 1, "product_group" => "dyno")
|
15
|
+
unit_group1 = Shushu::UnitGroup.new([unit1, unit2])
|
16
|
+
line_item = Shushu::LineItem.new([unit_group1])
|
17
|
+
lip = Shushu::LineItemPresenter.new(line_item)
|
18
|
+
assert_equal("11.000", lip.unit_group_qty("dyno"))
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_unit_group_presenters
|
22
|
+
unit1 = Shushu::BillableUnit.new("product_group" => "dyno")
|
23
|
+
unit2 = Shushu::BillableUnit.new("product_group" => "dyno")
|
24
|
+
unit3 = Shushu::BillableUnit.new("product_group" => "addon")
|
25
|
+
unit_group1 = Shushu::UnitGroup.new([unit1, unit2])
|
26
|
+
unit_group2 = Shushu::UnitGroup.new([unit3])
|
27
|
+
line_item = Shushu::LineItem.new([unit_group1, unit_group2])
|
28
|
+
lip = Shushu::LineItemPresenter.new(line_item)
|
29
|
+
|
30
|
+
assert_equal(2, lip.unit_group_presenters.length)
|
31
|
+
assert_equal(1, lip.unit_group_presenters("dyno").map(&:product_group).uniq.length)
|
32
|
+
assert_equal("addon", lip.unit_group_presenters("addon").map(&:product_group).uniq.first)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class ReportPresenterTest < ShushuTest
|
4
|
+
|
5
|
+
def report
|
6
|
+
Shushu::Report.new.tap do |r|
|
7
|
+
r.billable_units = Fixtures.billable_units.map {|h| Shushu::BillableUnit.new(h)}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_init
|
12
|
+
presenter = Shushu::ReportPresenter.new(report)
|
13
|
+
assert(!presenter.nil?)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_returns_line_item_presenters
|
17
|
+
presenter = Shushu::ReportPresenter.new(report)
|
18
|
+
lip = presenter.line_item_presenters
|
19
|
+
assert_equal(Shushu::LineItemPresenter, lip.first.class)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.expand_path('../../test_helper', __FILE__)
|
2
|
+
|
3
|
+
class LineItemBuilderTest < ShushuTest
|
4
|
+
|
5
|
+
def units
|
6
|
+
Fixtures.billable_units.map {|h| Shushu::BillableUnit.new(h)}
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_build
|
10
|
+
line_items = Shushu::LineItemBuilder.build(units)
|
11
|
+
assert(!line_items.nil?)
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_build_creates_line_item_by_hid
|
15
|
+
line_items = Shushu::LineItemBuilder.build(units)
|
16
|
+
assert_equal(1, line_items.length)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_build_creates_one_unit_group_for_line_item
|
20
|
+
line_item = Shushu::LineItemBuilder.build(units).pop
|
21
|
+
assert_equal(1, line_item.unit_groups.length)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_build_puts_both_units_on_unit_group
|
25
|
+
line_item = Shushu::LineItemBuilder.build(units).pop
|
26
|
+
unit_group = line_item.unit_groups.pop
|
27
|
+
assert_equal(2, unit_group.units.length)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
$:.unshift("lib")
|
2
|
+
$:.unshift("test")
|
3
|
+
|
4
|
+
ENV["RACK_ENV"] = "test"
|
5
|
+
|
6
|
+
require "rubygems"
|
7
|
+
require "bundler"
|
8
|
+
Bundler.require(:default, :test)
|
9
|
+
|
10
|
+
require "minitest/autorun"
|
11
|
+
require "shushu"
|
12
|
+
require "fakeweb"
|
13
|
+
require "fixtures"
|
14
|
+
|
15
|
+
puts "Blocking network connectivity"
|
16
|
+
FakeWeb.allow_net_connect = false
|
17
|
+
|
18
|
+
class ShushuTest < MiniTest::Unit::TestCase
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: shushu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.5
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryan Smith
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-29 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rest-client
|
16
|
+
requirement: &26931360 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 1.6.7
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *26931360
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: yajl-ruby
|
27
|
+
requirement: &26930880 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.1.0
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *26930880
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: active_support
|
38
|
+
requirement: &26930500 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *26930500
|
47
|
+
description: A ruby wrapper around Shushu's HTTP API.
|
48
|
+
email: ryan@heroku.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- readme.md
|
54
|
+
- lib/presenters/base_presenter.rb
|
55
|
+
- lib/presenters/unit_presenter.rb
|
56
|
+
- lib/presenters/unit_group_presenter.rb
|
57
|
+
- lib/presenters/report_presenter.rb
|
58
|
+
- lib/presenters/line_item_presenter.rb
|
59
|
+
- lib/shushu.rb
|
60
|
+
- lib/models/account.rb
|
61
|
+
- lib/models/acct_own.rb
|
62
|
+
- lib/models/report.rb
|
63
|
+
- lib/models/unit_group.rb
|
64
|
+
- lib/models/rev_rep.rb
|
65
|
+
- lib/models/line_item.rb
|
66
|
+
- lib/models/heart_beat.rb
|
67
|
+
- lib/models/r_code.rb
|
68
|
+
- lib/models/res_own.rb
|
69
|
+
- lib/models/b_event.rb
|
70
|
+
- lib/models/billable_unit.rb
|
71
|
+
- lib/helpers/http_helpers.rb
|
72
|
+
- lib/services/line_item_builder.rb
|
73
|
+
- test/presenters/line_item_presenter_test.rb
|
74
|
+
- test/presenters/report_presenter_test.rb
|
75
|
+
- test/fixtures.rb
|
76
|
+
- test/models/r_code_test.rb
|
77
|
+
- test/models/billable_unit_test.rb
|
78
|
+
- test/models/b_event_test.rb
|
79
|
+
- test/models/unit_group_test.rb
|
80
|
+
- test/models/rev_rep_test.rb
|
81
|
+
- test/models/line_item_test.rb
|
82
|
+
- test/services/line_item_builder_test.rb
|
83
|
+
- test/test_helper.rb
|
84
|
+
homepage: http://github.com/heroku/shushu
|
85
|
+
licenses: []
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 1.8.10
|
105
|
+
signing_key:
|
106
|
+
specification_version: 3
|
107
|
+
summary: Shushu client lib
|
108
|
+
test_files:
|
109
|
+
- test/presenters/line_item_presenter_test.rb
|
110
|
+
- test/presenters/report_presenter_test.rb
|
111
|
+
- test/models/r_code_test.rb
|
112
|
+
- test/models/billable_unit_test.rb
|
113
|
+
- test/models/b_event_test.rb
|
114
|
+
- test/models/unit_group_test.rb
|
115
|
+
- test/models/rev_rep_test.rb
|
116
|
+
- test/models/line_item_test.rb
|
117
|
+
- test/services/line_item_builder_test.rb
|