shushu 0.1.16 → 1.0.0rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,12 +2,6 @@ module Shushu
2
2
  module HttpHelpers
3
3
  extend self
4
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
5
  def headers
12
6
  {
13
7
  :content_type => :json,
@@ -1,30 +1,25 @@
1
1
  module Shushu
2
- module BEvent
3
- extend self
2
+ class BillableEvent < Client
4
3
 
5
4
  OPEN = "open"
6
5
  CLOSE = "close"
7
6
 
8
- def all
9
- Shushu.handle_req {RestClient.get([Shushu.url, "/billable_events"].join, Shushu.headers)}
10
- end
11
-
12
7
  def open(args)
13
8
  hid = args.delete(:hid) || args.delete("hid")
14
9
  entity_id = args.delete(:entity_id) || args.delete("entity_id")
15
10
  args[:state] = OPEN
16
- Shushu.handle_req {RestClient.put(events_url(hid, entity_id), args, Shushu.headers)}
11
+ handle_req {RestClient.put(events_url(hid, entity_id), args, headers)}
17
12
  end
18
13
 
19
14
  def close(args)
20
15
  hid = args.delete(:hid) || args.delete("hid")
21
16
  entity_id = args.delete(:entity_id) || args.delete("entity_id")
22
17
  args[:state] = CLOSE
23
- Shushu.handle_req {RestClient.put(events_url(hid, entity_id), args, Shushu.headers)}
18
+ handle_req {RestClient.put(events_url(hid, entity_id), args, headers)}
24
19
  end
25
20
 
26
21
  def events_url(hid, entity_id)
27
- [Shushu.url, "/resources/#{hid}/billable_events/#{entity_id}"].join
22
+ [url, "/resources/#{hid}/billable_events/#{entity_id}"].join
28
23
  end
29
24
 
30
25
  end
@@ -0,0 +1,20 @@
1
+ module Shushu
2
+ class RateCode < Client
3
+ def create(args)
4
+ if args[:slug].nil?
5
+ handle_req do
6
+ RestClient.post(rate_codes_url, args, headers)
7
+ end
8
+ else
9
+ handle_req do
10
+ url = [rate_codes_url, args.delete(:slug)].join("/")
11
+ RestClient.put(url, args, headers)
12
+ end
13
+ end
14
+ end
15
+
16
+ def rate_codes_url
17
+ [url, "/rate_codes"].join
18
+ end
19
+ end
20
+ end
data/lib/models/report.rb CHANGED
@@ -1,68 +1,50 @@
1
+ require 'ostruct'
2
+
1
3
  module Shushu
2
- class Report
4
+ class Report < Client
3
5
  attr_accessor(
4
6
  :from,
5
7
  :to,
6
- :billable_units
8
+ :billable_units,
9
+ :report
7
10
  )
8
11
 
9
- def fetch
10
- url = [Shushu.url, resource].join
11
- Shushu.handle_req do
12
- RestClient.get(url, {:params => {
13
- :from => from.utc.to_s,
14
- :to => to.utc.to_s
15
- }})
12
+ def fetch(report_url, from, to)
13
+ @report = handle_req do
14
+ response_found = false
15
+ while !response_found
16
+ response = RestClient::Resource.new(report_url).get({:params => {
17
+ :from => from.utc.to_s,
18
+ :to => to.utc.to_s
19
+ }})
20
+
21
+ if response.code == 202
22
+ sleep 0.5
23
+ else
24
+ response_found = true
25
+ end
26
+ end
27
+ response
16
28
  end
29
+ self
17
30
  end
18
31
 
19
32
  def billable_units
20
- @billable_units ||= report["billable_units"].map {|buh| BillableUnit.new(buh)}
33
+ @billable_units ||= report["billable_units"].map do |unit_hash|
34
+ OpenStruct.new(unit_hash).tap { |o| o.extend Totalable }
35
+ end
21
36
  end
22
37
 
23
38
  def total
24
39
  report["total"]
25
40
  end
26
-
27
- def report
28
- @report ||= fetch
29
- end
30
- end
31
-
32
- class UsageReport < Report
33
- attr_accessor :account_id
34
-
35
- def initialize(account_id, from, to)
36
- @account_id, @from, @to = account_id, from, to
37
- end
38
-
39
- def resource
40
- "/accounts/#{@account_id}/usage_reports"
41
- end
42
- end
43
-
44
- class Invoice < Report
45
- attr_accessor :payment_method_id
46
-
47
- def initialize(payment_method_id, from, to)
48
- @payment_method_id, @from, @to = payment_method_id, from, to
49
- end
50
-
51
- def resource
52
- "/payment_methods/#{payment_method_id}/invoices"
53
- end
54
41
  end
55
42
 
56
43
  class RateCodeReport < Report
57
- attr_accessor :rate_code_slug
58
-
59
- def initialize(rate_code_slug, from, to)
60
- @rate_code_slug, @from, @to = rate_code_slug, from, to
61
- end
44
+ attr_accessor :slug
62
45
 
63
- def resource
64
- "/rate_codes/#{rate_code_slug}/report"
46
+ def fetch(slug, from, to)
47
+ super(self.url + "/rate_codes/#{slug}", from, to)
65
48
  end
66
49
  end
67
-
68
50
  end
File without changes
data/lib/shushu.rb CHANGED
@@ -11,60 +11,34 @@ module Inflector
11
11
  end
12
12
 
13
13
  module Shushu
14
- extend self
15
- extend HttpHelpers
16
-
17
14
  ShushuException = Class.new(StandardError)
18
15
 
19
- def url
20
- Client.url
21
- end
22
-
23
- def url=(url)
24
- Client.url = url
25
- end
26
-
27
16
  class Client
28
- def self.url=(url)
29
- @@url = url
30
- end
17
+ include HttpHelpers
31
18
 
32
- def self.url
33
- if defined?(@@url)
34
- @@url
35
- else
36
- ENV["SHUSHU_URL"]
37
- end
38
- end
19
+ attr_accessor :url
39
20
 
40
- def initialize(url)
41
- self.class.url = url
21
+ def initialize(url = ENV["SHUSHU_URL"])
22
+ @url = url
42
23
  end
43
24
 
44
- def [](api)
45
- Shushu.const_get(Inflector.camelize(api))
25
+ def method_missing(method, *args, &block)
26
+ begin
27
+ klass = method.to_s.gsub(/s$/,'')
28
+ Shushu.const_get(Inflector.camelize(klass)).new(@url)
29
+ rescue NameError => e
30
+ raise NameError, "'#{method}' not a local variable, method, or Shushu classname"
31
+ end
46
32
  end
47
33
  end
48
34
  end
49
35
 
36
+ # for ease of typing
37
+ ShuShu = Shushu
50
38
 
51
- require "models/account"
52
- require "models/billable_unit"
39
+ require "totalable"
40
+ require "models/billable_event"
41
+ require "models/resource_ownership"
42
+ require "models/rate_code"
53
43
  require "models/report"
54
- require "models/unit_group"
55
- require "models/line_item"
56
- require "models/b_event"
57
- require "models/res_own"
58
- require "models/r_code"
59
- require "models/rev_rep"
60
44
  require "models/heart_beat"
61
- require "models/p_method"
62
- require "models/acct_own"
63
-
64
- require "services/line_item_builder"
65
-
66
- require "presenters/base_presenter"
67
- require "presenters/unit_group_presenter"
68
- require "presenters/unit_presenter"
69
- require "presenters/line_item_presenter"
70
- require "presenters/report_presenter"
data/lib/totalable.rb ADDED
@@ -0,0 +1,7 @@
1
+ module Shushu
2
+ module Totalable
3
+ def total
4
+ rate.to_f * qty.to_f
5
+ end
6
+ end
7
+ end
data/readme.md CHANGED
@@ -13,109 +13,13 @@ $ export SHUSHU_URL=https://123:secret@shushu.heroku.com
13
13
 
14
14
  ## Usage
15
15
 
16
- There are two ways to interact with Shushu APIs using this client. The first
17
- approach will rely on the credentials in `SHUSHU_URL`.
16
+ Instantiate a client to interact with the Shushu API.
18
17
 
19
- ```ruby
20
- Shushu::BEvent.open({})
21
- ```
22
-
23
- The second approach allows for credentials other than the ones stored in the
24
- `SHUSHU_URL`.
18
+ `Client::new` defaults to using `ENV['SHUHSU_URL']` if one is not provided.
25
19
 
26
20
  ```ruby
27
21
  shushu = Shushu::Client.new("https://other_provider_id:other_provider_token@shushu.heroku.com")
28
- shushu[:b_event].open({})
29
- ```
30
-
31
- ### PaymentMethod
32
-
33
- This API deals primarily with credit cards. PaymentMethods can be created
34
- indipendintly of Accounts. You will need a payment_method to generate an
35
- invoice.
36
-
37
- Reference: [PaymentMethod API](https://github.com/heroku/shushud/blob/master/doc/payment_methods_api.md)
38
-
39
- Create a new payment method with encrypted values:
40
-
41
- ```ruby
42
- PMethod.create(
43
- :card_num => "some encrypted value",
44
- :card_exp_year => "2012",
45
- :card_exp_month => "12"
46
- )
47
- #=> {:id => "12345", :card_token => "abc123", :card_type => "visa", :card_last4 => "1111"}
48
- ```
49
-
50
- Update a payment_method with a new credit card:
51
-
52
- ```ruby
53
- PMethod.update(
54
- :id => "001",
55
- :card_num => "some encrypted value",
56
- :card_exp_year => "2012",
57
- :card_exp_month => "12"
58
- )
59
- #=> {:id => "001", :card_token => "abc123", :card_type => "visa", :card_last4 => "1111"}
60
- ```
61
-
62
- Create a new payment_method with a token. Note, this endpoint is
63
- idempotent with respect to the id and token.
64
-
65
- ```ruby
66
- PMethod.update(:id => "my-custom-id", :card_token => "abc123")
67
- #=> {:id => "my-custom-id", :card_token => "abc123"}
68
- ```
69
-
70
- To mark a payment_method in such a way that credit card processing will be
71
- disabled:
72
-
73
- ```ruby
74
- PMethod.update(:id => "001", :non_receivable => true)
75
- #=> {:id => "001", :non_receivable => true}
76
- ```
77
-
78
- ### Account
79
-
80
- This API deals with accounts which is a primitive for grouping resources. You
81
- will need an account to generate a usage report.
82
-
83
- ```ruby
84
- Shushu::Account.create
85
- #=> {:id => "001"}
86
- ```
87
-
88
- ### AccountOwnership
89
-
90
- Use this API when you want to setup associations between Shushu accounts and
91
- Shushu payment_methods.
92
-
93
- Reference: [AccountOwnerships API](https://github.com/heroku/shushud/blob/master/doc/account_ownership_api.md)
94
-
95
- To associate an account with a payment_method:
96
-
97
- ```ruby
98
- Shushu::AcctOwn.act(
99
- :account_id => vault_account_id,
100
- :payment_method_id => payment_method_id,
101
- :entity_id => entity_id,
102
- :time => time
103
- )
104
- #=> {"payment_method_id"=>"123", "account_id"=>"1", "entity_id"=>"entity123"}
105
- ```
106
-
107
- If you need to move one account to another payment_method, you will need to
108
- deactivate the previous relationship and activate a new one. To deactivate
109
- call this method:
110
-
111
- ```ruby
112
- Shushu::AcctOwn.deact(
113
- :account_id => vault_account_id,
114
- :payment_method_id => payment_method_id,
115
- :entity_id => entity_id,
116
- :time => time
117
- )
118
- #=> {"payment_method_id"=>"123", "account_id"=>"1", "entity_id"=>"entity123"}
22
+ shushu.billable_events.open({})
119
23
  ```
120
24
 
121
25
  ### RateCode
@@ -126,7 +30,7 @@ RateCode. You can provide your own slug or a UUID will be generated for you. If
126
30
  Reference: [RateCode API](https://github.com/heroku/shushud/blob/master/doc/rate_code_api.md)
127
31
 
128
32
  ```ruby
129
- Shushu::RCode.create(
33
+ client.rate_codes.create(
130
34
  :slug => some_record.uuid,
131
35
  :rate => 5,
132
36
  :group => "addon",
@@ -148,7 +52,7 @@ Let Shushu generate a UUID for the slug.
148
52
  Remember to save the slug somewhere so that you can present it with billable_events.
149
53
 
150
54
  ```ruby
151
- Shushu::RCode.create(
55
+ client.rate_codes.create(
152
56
  :rate => 5,
153
57
  :group => "addon",
154
58
  :period => "month",
@@ -176,7 +80,7 @@ When the app is created or it is joining a new account, activate the the
176
80
  resource_ownership record.
177
81
 
178
82
  ```ruby
179
- Shushu::ResOwn.act(
83
+ client.resource_ownerships.act(
180
84
  :resource_id => resource_id,
181
85
  :entity_id => entity_id,
182
86
  :account_id => vault_account_id,
@@ -189,7 +93,7 @@ When an app is destroyed, or the app is moving to a new account_id,
189
93
  deactivate the resource_ownership record.
190
94
 
191
95
  ```ruby
192
- Shushu::ResOwn.deact(
96
+ client.resource_ownerships.deact(
193
97
  :resource_id => resource_id,
194
98
  :entity_id => entity_id,
195
99
  :account_id => vault_account_id,
@@ -210,7 +114,7 @@ Reference: [BillableEvent API](https://github.com/heroku/shushud/blob/master/doc
210
114
  Open an event when you would like to start billing.
211
115
 
212
116
  ```ruby
213
- Shushu::BEvent.open(
117
+ client.billable_events.open(
214
118
  :entity_id => entity_id,
215
119
  :hid => hid,
216
120
  :time => time,
@@ -224,7 +128,7 @@ Shushu::BEvent.open(
224
128
  Don't forget to close it.
225
129
 
226
130
  ```ruby
227
- Shushu::BEvent.close(
131
+ client.billable_events.close(
228
132
  :entity_id => entity_id,
229
133
  :hid => hid,
230
134
  :rate_code => rate_code,
@@ -232,88 +136,10 @@ Shushu::BEvent.close(
232
136
  )
233
137
  ```
234
138
 
235
- ### Invoice
236
-
237
- ```ruby
238
- invoice = Shushu::Invoice.new(payment_method_id, from, to)
239
- invoice.billable_units
240
- ```
241
- ### UsageReport
242
-
243
- ```ruby
244
- report = Shushu::UsageReport.new(account_id, from, to)
245
- report.billable_units
246
- ```
247
-
248
139
  ### RateCodeReport
249
140
 
250
141
  ```ruby
251
- report = Shushu::RateCodeReport.new(rate_code_slug, from, to)
142
+ report = client.rate_code_reports.fetch(rate_code_slug, from, to)
252
143
  report.total
253
144
  report.billable_units
254
145
  ```
255
-
256
-
257
- ### Report Generation
258
-
259
- Invoices and UsageReports can be used for report generation. Basically, the
260
- report generation code expects a collection of BillableUnits. BillableUnits are
261
- returned from both the Invoice API and the UsageReport API. However, the details
262
- of the billable_units may be different with respect to the type of the report.
263
-
264
- #### Presenters
265
-
266
- Clients of this library will want to generate some sort of view for the reports, the
267
- presenter objects were created to aid with that effort. You should only need to
268
- use the presenters while building views. Each view wraps a simple model object.
269
- All of the models and presenters are derived from the billable_unit which is
270
- retreived from the remote API.
271
-
272
- Report --> LineItem --> UnitGroup --> Billable Unit
273
-
274
- #### ReportPresenter
275
-
276
- The report presenter is how you will kick off the process of generating a
277
- report. You hand it a report object, either a UsageReport or and Invoice, and
278
- using the reports billable_units, it will build the line_items and a set of
279
- line_item_presenters for the line_items.
280
-
281
- #### LineItemBuilder
282
-
283
- The ReportPresenter will create a set of line_items based upon the
284
- billable_units in the report. The default is to group things by HID. Thus there
285
- will be a line_item for each distinct HID in the set of billable_units. The
286
- builder will also give a collection of unit_groups to the line_item. By default,
287
- the unit_groups will be partitioned by the product_group. (i.e. dyno, addon)
288
-
289
- You can customize the LineItemBuilder by creating a new class that responds to
290
- build and passing it to the ReportPresenter.
291
-
292
- ```ruby
293
- ReportPresenter.new(report, CustomLineItemBuilder)
294
- ```
295
-
296
- #### LineItemPresenter
297
-
298
- The LineItemPresenter is responsible for handling the total and names of the
299
- line_items. It also manages the set of unit_groups. Since the default
300
- LineItemBuilder partitioned unit_groups based upon product_group, you can ask
301
- the LineItemPresenter for infomation about subsets of unit_groups. For instance:
302
-
303
- ```ruby
304
- line_item_presenter.unit_group_presenters("dyno")
305
- line_item_presenter.unit_group_total("dyno")
306
- line_item_presenter.unit_group_qty("dyno")
307
- ```
308
- #### UnitGroupPresenter
309
-
310
- UnitGroups are collections of billable_units that are partitioned by
311
- product_name. So if there exists a set of billable_units that all have
312
- product_group = "dyno" and product_name= "worker", then this presenter
313
- will give you information about that group.
314
-
315
-
316
- #### UnitPresenter
317
-
318
- Finally, the UnitPresenter wraps a billable_unit and exposes methods to show
319
- totals, quantities and other meta-data.
@@ -0,0 +1,41 @@
1
+ require File.expand_path('../../test_helper', __FILE__)
2
+
3
+ class BillableEventTest < ShushuTest
4
+ def setup
5
+ @client = Shushu::Client.new "http://example.com"
6
+ super
7
+ end
8
+
9
+ def test_open
10
+ api_resp_body = Shushu::HttpHelpers.enc_json(our_params)
11
+ FakeWeb.register_uri(:put,
12
+ "http://example.com/resources/app123/billable_events/1",
13
+ :body => api_resp_body
14
+ )
15
+ event = @client.billable_events.open(our_params)
16
+ assert_equal(1, event["entity_id"])
17
+ end
18
+
19
+ def test_close
20
+ api_resp_body = Shushu::HttpHelpers.enc_json(our_params)
21
+ FakeWeb.register_uri(:put,
22
+ "http://example.com/resources/app123/billable_events/1",
23
+ :body => api_resp_body
24
+ )
25
+ event = @client.billable_events.close(our_params)
26
+ assert_equal(1, event["entity_id"])
27
+ end
28
+
29
+ def our_params
30
+ {
31
+ :hid => "app123",
32
+ :entity_id => 1,
33
+ :rate_code => "RT01",
34
+ :time => Time.utc(2012,1).to_s,
35
+ :product_name => "web",
36
+ :description => "some command",
37
+ :qty => 1
38
+ }
39
+ end
40
+ end
41
+
@@ -1,6 +1,12 @@
1
1
  require File.expand_path('../../test_helper', __FILE__)
2
2
 
3
3
  class RCodeTest < ShushuTest
4
+ def setup
5
+ @url = "http://provider:password@shushu.heroku.com"
6
+ @client = Shushu::Client.new(@url)
7
+ super
8
+ end
9
+
4
10
  def test_create_without_slug
5
11
  our_params = {
6
12
  :rate => 5,
@@ -10,12 +16,11 @@ class RCodeTest < ShushuTest
10
16
  api_resp_body = Shushu::HttpHelpers.enc_json(
11
17
  our_params.merge(:slug => "AO01")
12
18
  )
13
- Shushu.url = "http://provider:password@shushu.heroku.com"
14
19
  FakeWeb.register_uri(:post,
15
- (Shushu.url + "/rate_codes"),
20
+ (@url + "/rate_codes"),
16
21
  :body => api_resp_body
17
22
  )
18
- rate_code = Shushu::RCode.create(our_params)
23
+ rate_code = @client.rate_codes.create(our_params)
19
24
  assert_equal("AO01", rate_code["slug"])
20
25
  end
21
26
 
@@ -30,14 +35,12 @@ class RCodeTest < ShushuTest
30
35
  api_resp_body = Shushu::HttpHelpers.enc_json(
31
36
  our_params.merge(:slug => slug)
32
37
  )
33
- Shushu.url = "http://provider:password@shushu.heroku.com"
34
38
  FakeWeb.register_uri(:put,
35
- (Shushu.url + "/rate_codes/#{slug}"),
39
+ (@url + "/rate_codes/#{slug}"),
36
40
  :body => api_resp_body
37
41
  )
38
- rate_code = Shushu::RCode.create(our_params)
42
+ rate_code = @client.rate_codes.create(our_params)
39
43
  assert_equal(slug, rate_code["slug"])
40
-
41
44
  end
42
45
  end
43
46
 
@@ -0,0 +1,19 @@
1
+ require File.expand_path('../test_helper', __FILE__)
2
+
3
+ class RateCodeReportTest < ShushuTest
4
+ def setup
5
+ @url = "http://provider:password@shushu.heroku.com"
6
+ @client = Shushu::Client.new(@url)
7
+ super
8
+ end
9
+
10
+ def test_rate_code_report
11
+ FakeWeb.register_uri(:get,
12
+ Regexp.new(Regexp.escape(@url + "/rate_codes/GUID")),
13
+ :body => Shushu::HttpHelpers.enc_json({:billable_units => [{"rate" => 10, "qty" => 5}]})
14
+ )
15
+
16
+ report = @client.rate_code_reports.fetch("GUID", Time.mktime(2011,1,1), Time.mktime(2011,2,1))
17
+ assert_equal 50, report.billable_units.first.total
18
+ end
19
+ end