heroku-platform-api 0.0.1
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 +15 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +141 -0
- data/Rakefile +1 -0
- data/heroku.gemspec +26 -0
- data/lib/heroku/api.rb +19 -0
- data/lib/heroku/api/account.rb +38 -0
- data/lib/heroku/api/app.rb +61 -0
- data/lib/heroku/api/apps.rb +31 -0
- data/lib/heroku/api/password.rb +18 -0
- data/lib/heroku/api/rate_limits.rb +24 -0
- data/lib/heroku/api/regions.rb +13 -0
- data/lib/heroku/conn.rb +91 -0
- data/lib/heroku/conn/cache.rb +47 -0
- data/lib/heroku/model.rb +9 -0
- data/lib/heroku/model/account.rb +45 -0
- data/lib/heroku/model/app.rb +70 -0
- data/lib/heroku/model/app_list.rb +21 -0
- data/lib/heroku/model/array_proxy.rb +27 -0
- data/lib/heroku/model/model_helper.rb +17 -0
- data/lib/heroku/properties.rb +36 -0
- data/lib/heroku/properties/null_logger.rb +16 -0
- data/lib/heroku/version.rb +3 -0
- data/lib/heroku_api.rb +6 -0
- data/spec/heroku/conn/cache_spec.rb +29 -0
- data/spec/heroku/conn_spec.rb +14 -0
- data/spec/heroku/model/model_helper_spec.rb +70 -0
- data/spec/heroku/properties/null_logger_spec.rb +25 -0
- data/spec/heroku/properties_spec.rb +45 -0
- data/spec/spec_helper.rb +15 -0
- metadata +141 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZTQ3NDk4YzM0YjRjNGYwZmM5NmZkYTkzMjBmYTI2NjU5M2FkYTY2ZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NGVhZmFmYjZlOWExNDNiNDRhOGY5Y2I5ZDI2N2I5NWRmMjdhMTNiNA==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
OTY5YWYxOWQ5Y2RiYmMwOWEzMDZkM2FjYmZjY2JhMWY1N2NhNjA1NTg5NmRl
|
10
|
+
OWIxNDJmMTY0ZDIxYWJkOGNmZjRmNzBlMmFhOWQ5MWUxODc0NjBlNmQ0NjQw
|
11
|
+
OWZhMmM4YWZiY2RmMDg4NWQ3NGU3OTRjZjc0YWNkYTAwY2U1OTA=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YTUyYjg1NTY5NWM2YTM2M2YzYzAyNGNkMDY1OGI5YzZjMjc1NTM5YWI4MGE5
|
14
|
+
YWUzY2RkNDc2MjdkMjU4YzE1OWMwZWQ3NTE4NjI3NDhhYzYxNGQwMjBlNGUz
|
15
|
+
YTJhNjNlZjY0NDljYzQ0OTQ5NDE0M2U5ZGY3MDYzNzU0ZThhY2Y=
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Ashok Menon
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
# Heroku
|
2
|
+
|
3
|
+
Create, destroy and manage your heroku applications programmatically, using the Heroku Platform API.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'heroku-platform-api'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install heroku-platform-api
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
### Require the gem
|
22
|
+
|
23
|
+
require 'heroku_api'
|
24
|
+
# => true
|
25
|
+
|
26
|
+
### Configuration
|
27
|
+
|
28
|
+
Heroku::API.configure do |c|
|
29
|
+
c.api_key = "<Heroku API token>" # Mandatory
|
30
|
+
c.logger = Logger.new($stdout) # Optional
|
31
|
+
end
|
32
|
+
|
33
|
+
### Apps
|
34
|
+
|
35
|
+
The `Heroku::API.apps` object can be treated as though it were an `Array` of applications
|
36
|
+
with the below methods added.
|
37
|
+
|
38
|
+
#### Creating a new App
|
39
|
+
|
40
|
+
app = Heroku::API.apps.new(name: "example", region: {name: 'us'}, stack: 'cedar')
|
41
|
+
# => #<Heroku::Model::App id="01234567-89ab-cdef-0123-456789abcdef", name="example">
|
42
|
+
|
43
|
+
All the parameters provided are optional, if this end-point is called without them,
|
44
|
+
then defaults will be chosen.
|
45
|
+
|
46
|
+
#### Listing all Available Apps
|
47
|
+
|
48
|
+
Heroku::API.apps.all
|
49
|
+
# => [#<Heroku::Model App...>,...]
|
50
|
+
|
51
|
+
#### Searching for a Particular App
|
52
|
+
|
53
|
+
Heroku::API.apps["example"]
|
54
|
+
# => #<Heroku::Model::App id="01234567-89ab-cdef-0123-456789abcdef", name="example">
|
55
|
+
|
56
|
+
OR
|
57
|
+
|
58
|
+
Heroku::API.apps.app("example")
|
59
|
+
# => #<Heroku::Model::App id="01234567-89ab-cdef-0123-456789abcdef", name="example">
|
60
|
+
|
61
|
+
#### Updating an Existing App
|
62
|
+
|
63
|
+
app.name = "my_app" # Name and heroku based sub-domain of app.
|
64
|
+
app.maintenance = true # Maintenance mode on.
|
65
|
+
app.save
|
66
|
+
# => #<Heroku::Model::App id="01234567-89ab-cdef-0123-456789abcdef", name="my_app">
|
67
|
+
|
68
|
+
#### Pushing to an Existing App
|
69
|
+
|
70
|
+
This gem provides rudimentary support for pushing a given git repo to be deployed
|
71
|
+
as the app, by simplying providing the directory of the repo:
|
72
|
+
|
73
|
+
app.push("path/to/repository")
|
74
|
+
# => true
|
75
|
+
|
76
|
+
### Account
|
77
|
+
|
78
|
+
#### Getting the account details
|
79
|
+
|
80
|
+
acc = Heroku::API.account
|
81
|
+
# => #<Heroku::Model::Account id="01234567-89ab-cdef-0123-456789abcdef", email="username@example.com">
|
82
|
+
|
83
|
+
#### Updating the account details
|
84
|
+
|
85
|
+
acc.email = "joe-bloggs@example.com"
|
86
|
+
acc.allow_tracking = false # Let third party tracking services track you.
|
87
|
+
acc.save
|
88
|
+
# => #<Heroku::Model::Account id="01234567-89ab-cdef-0123-456789abcdef", email="joe-bloggs@example.com">
|
89
|
+
|
90
|
+
#### Changing the account password
|
91
|
+
|
92
|
+
acc.update_password("new_password", "old_password")
|
93
|
+
# => true
|
94
|
+
|
95
|
+
OR
|
96
|
+
|
97
|
+
Heroku::API.update_password("new_password", "old_password")
|
98
|
+
# => true
|
99
|
+
|
100
|
+
### Rate Limits
|
101
|
+
|
102
|
+
You can check the number of requests left like so:
|
103
|
+
|
104
|
+
Heroku::API.account.rate_limits
|
105
|
+
# => 1200
|
106
|
+
|
107
|
+
OR
|
108
|
+
|
109
|
+
Heroku::API.rate_limits
|
110
|
+
# => 1200
|
111
|
+
|
112
|
+
### Regions
|
113
|
+
|
114
|
+
Find out the available regions with:
|
115
|
+
|
116
|
+
Heroku::API.regions
|
117
|
+
# => [{"created_at"=>"2012-11-21T21:44:16Z", "description"=>"United States", "id"=>"59accabd-516d-4f0e-83e6-6e3757701145", "name"=>"us", "updated_at"=>"2013-04-05T10:13:06Z"}, {"created_at"=>"2012-11-21T22:05:26Z", "description"=>"Europe", "id"=>"ed30241c-ed8c-4bb6-9714-61953675d0b4", "name"=>"eu", "updated_at"=>"2013-04-05T07:07:28Z"}]
|
118
|
+
|
119
|
+
### Further API endpoints
|
120
|
+
|
121
|
+
The Heroku Platform API Gem does not currently support any further API end-points
|
122
|
+
natively. If you would like to add them, feel free to contribute, as directed below.
|
123
|
+
|
124
|
+
If you would like to test the various endpoints that are not currently fully
|
125
|
+
supported, please use the `Heroku::Conn` class, which will return the requested
|
126
|
+
data as Ruby Arrays and Hashes:
|
127
|
+
|
128
|
+
*Raw request for rate limit*
|
129
|
+
|
130
|
+
etag, response = Heroku::Conn::Get('/account/rate-limits'); response
|
131
|
+
# => { 'remaining' => '1200' }
|
132
|
+
|
133
|
+
For further information, visit the [Heroku Platform API](https://devcenter.heroku.com/articles/platform-api-reference).
|
134
|
+
|
135
|
+
## Contributing
|
136
|
+
|
137
|
+
1. Fork it
|
138
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
139
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
140
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
141
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/heroku.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'heroku/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "heroku-platform-api"
|
8
|
+
spec.version = Heroku::VERSION
|
9
|
+
spec.authors = ["Ashok Menon", "Rentify"]
|
10
|
+
spec.email = ["amenon94@gmail.com", "dev@rentify.com"]
|
11
|
+
spec.description = %q{Create, destroy and manage your heroku applications programmatically, using the Heroku Platform API.}
|
12
|
+
spec.summary = %q{Ruby client for the Heroku Platform API.}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "git"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "rspec"
|
26
|
+
end
|
data/lib/heroku/api.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'heroku/properties'
|
2
|
+
|
3
|
+
module Heroku
|
4
|
+
class API
|
5
|
+
require 'heroku/api/account'
|
6
|
+
require 'heroku/api/password'
|
7
|
+
require 'heroku/api/rate_limits'
|
8
|
+
require 'heroku/api/regions'
|
9
|
+
require 'heroku/api/apps'
|
10
|
+
require 'heroku/api/app'
|
11
|
+
|
12
|
+
extend Heroku::Properties::ConfigMethods
|
13
|
+
extend Heroku::API::Account
|
14
|
+
extend Heroku::API::Password
|
15
|
+
extend Heroku::API::RateLimits
|
16
|
+
extend Heroku::API::Regions
|
17
|
+
extend Heroku::API::Apps
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'heroku/conn'
|
2
|
+
require 'heroku/properties'
|
3
|
+
require 'heroku/model/account'
|
4
|
+
|
5
|
+
module Heroku
|
6
|
+
class API
|
7
|
+
module Account
|
8
|
+
@@etag = nil
|
9
|
+
RESOURCE_TYPE = "ACCOUNT"
|
10
|
+
|
11
|
+
def account
|
12
|
+
Heroku::Properties.logger.info("[Account] Fetching.")
|
13
|
+
|
14
|
+
@@etag, res =
|
15
|
+
Heroku::Conn::Get(
|
16
|
+
'/account',
|
17
|
+
etag: @@etag,
|
18
|
+
r_type: RESOURCE_TYPE
|
19
|
+
)
|
20
|
+
|
21
|
+
Heroku::Model::Account.new(res.merge("parent" => self))
|
22
|
+
end
|
23
|
+
|
24
|
+
def update_account(account)
|
25
|
+
Heroku::Properties.logger.info("[Account] Updating #{account.id}")
|
26
|
+
|
27
|
+
@@etag, res =
|
28
|
+
Heroku::Conn::Patch(
|
29
|
+
"/account",
|
30
|
+
r_type: RESOURCE_TYPE,
|
31
|
+
body: account.patchable.to_json
|
32
|
+
)
|
33
|
+
|
34
|
+
Heroku::Model::Account.new(res.merge("parent" => self))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'heroku/conn'
|
2
|
+
require 'heroku/properties'
|
3
|
+
require 'heroku/model/app'
|
4
|
+
|
5
|
+
module Heroku
|
6
|
+
class API
|
7
|
+
module App
|
8
|
+
@@etags = {}
|
9
|
+
RESOURCE_TYPE = "APP"
|
10
|
+
|
11
|
+
def app(name_or_id)
|
12
|
+
Heroku::Properties.logger.info("[App] Fetching #{name_or_id}")
|
13
|
+
|
14
|
+
etag, res =
|
15
|
+
Heroku::Conn::Get(
|
16
|
+
"/apps/#{name_or_id}",
|
17
|
+
etag: @@etags[name_or_id],
|
18
|
+
r_type: RESOURCE_TYPE
|
19
|
+
)
|
20
|
+
|
21
|
+
@@etags[res['id']] = etag
|
22
|
+
@@etags[res['name']] = etag
|
23
|
+
Heroku::Model::App.new(res.merge("parent" => self))
|
24
|
+
end
|
25
|
+
|
26
|
+
def new(params = {})
|
27
|
+
Heroku::Properties.logger.info("[App] New with parameters: #{params.inspect}")
|
28
|
+
|
29
|
+
_, res =
|
30
|
+
Heroku::Conn::Post(
|
31
|
+
'/apps',
|
32
|
+
r_type: RESOURCE_TYPE,
|
33
|
+
body: params.to_json
|
34
|
+
)
|
35
|
+
|
36
|
+
Heroku::Model::App.new(res.merge("parent" => self))
|
37
|
+
end
|
38
|
+
|
39
|
+
def update_app(app)
|
40
|
+
Heroku::Properties.logger.info("[App] Updating #{app.id}")
|
41
|
+
|
42
|
+
etag, res =
|
43
|
+
Heroku::Conn::Patch(
|
44
|
+
app.end_point,
|
45
|
+
r_type: RESOURCE_TYPE,
|
46
|
+
body: app.patchable.to_json
|
47
|
+
)
|
48
|
+
|
49
|
+
@@etags[res['id']] = etag
|
50
|
+
@@etags[res['name']] = etag
|
51
|
+
Heroku::Model::App.new(res.merge("parent" => self))
|
52
|
+
end
|
53
|
+
|
54
|
+
def delete_app(app)
|
55
|
+
Heroku::Properties.logger.info("[App] Deleting #{app.id}")
|
56
|
+
Heroku::Conn::Delete(app.end_point, r_type: RESOURCE_TYPE)
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'heroku/conn'
|
2
|
+
require 'heroku/properties'
|
3
|
+
require 'heroku/model/app_list'
|
4
|
+
require 'heroku/model/app'
|
5
|
+
|
6
|
+
module Heroku
|
7
|
+
class API
|
8
|
+
module Apps
|
9
|
+
@@etag = nil
|
10
|
+
RESOURCE_TYPE = "APPS"
|
11
|
+
|
12
|
+
def apps
|
13
|
+
Heroku::Model::AppList.new( ->(parent){
|
14
|
+
Heroku::Properties.logger.info("[Apps] Fetching")
|
15
|
+
|
16
|
+
@@etag, res =
|
17
|
+
Heroku::Conn::Get(
|
18
|
+
"/apps",
|
19
|
+
etag: @@etag,
|
20
|
+
r_type: RESOURCE_TYPE
|
21
|
+
)
|
22
|
+
|
23
|
+
res.map do |params|
|
24
|
+
Heroku::Model::App.new(params.merge("parent" => parent))
|
25
|
+
end
|
26
|
+
})
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'heroku/conn'
|
2
|
+
require 'heroku/properties'
|
3
|
+
|
4
|
+
module Heroku
|
5
|
+
class API
|
6
|
+
module Password
|
7
|
+
def update_password(new_password, current_password)
|
8
|
+
Heroku::Properties.logger.info("[Password] Updating")
|
9
|
+
|
10
|
+
Heroku::Conn::Put("/account/password", body: {
|
11
|
+
password: new_password,
|
12
|
+
current_password: current_password
|
13
|
+
}.to_json)
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'heroku/conn'
|
2
|
+
require 'heroku/properties'
|
3
|
+
|
4
|
+
module Heroku
|
5
|
+
class API
|
6
|
+
module RateLimits
|
7
|
+
@@etag = nil
|
8
|
+
RESOURCE_TYPE = "RATE_LIMITS"
|
9
|
+
|
10
|
+
def rate_limits
|
11
|
+
Heroku::Properties.logger.info("[Rate Limits] Fetching")
|
12
|
+
|
13
|
+
@@etag, res =
|
14
|
+
Heroku::Conn::Get(
|
15
|
+
"/account/rate-limits",
|
16
|
+
etag: @@etag,
|
17
|
+
r_type: RESOURCE_TYPE
|
18
|
+
)
|
19
|
+
|
20
|
+
res["remaining"].to_i
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/heroku/conn.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require 'heroku/properties'
|
4
|
+
|
5
|
+
module Heroku
|
6
|
+
class Conn
|
7
|
+
require 'heroku/conn/cache'
|
8
|
+
|
9
|
+
APIRequest = Struct.new(:method, :end_point)
|
10
|
+
|
11
|
+
@https = Net::HTTP.new('api.heroku.com', 443).tap do |https|
|
12
|
+
https.use_ssl = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.cache
|
16
|
+
@cache ||= Heroku::Conn::Cache.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.method_missing(method, end_point, opts = {})
|
20
|
+
_Request = Net::HTTP.const_get(method.capitalize)
|
21
|
+
|
22
|
+
req = _Request.new(end_point, headers(opts))
|
23
|
+
req.body = opts[:body]
|
24
|
+
api_req = APIRequest[method, end_point]
|
25
|
+
|
26
|
+
Heroku::Properties.logger.debug("[Conn] Attempting #{method.upcase} #{end_point} ...")
|
27
|
+
|
28
|
+
check_response(api_req, opts[:r_type], @https.request(req))
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def self.check_response(api_req, r_type, res)
|
34
|
+
Heroku::Properties.logger.debug("[Conn] Received #{res.code} for #{r_type} at #{api_req.end_point}")
|
35
|
+
|
36
|
+
case res
|
37
|
+
when Net::HTTPOK,
|
38
|
+
Net::HTTPCreated
|
39
|
+
cache.put(
|
40
|
+
r_type, res["ETag"],
|
41
|
+
JSON.parse(res.body)
|
42
|
+
)
|
43
|
+
when Net::HTTPPartialContent
|
44
|
+
cache.put(
|
45
|
+
r_type, res["ETag"],
|
46
|
+
gather_partial_content(api_req, res)
|
47
|
+
)
|
48
|
+
when Net::HTTPNotModified then cache.fetch(r_type, res["ETag"])
|
49
|
+
when Net::HTTPSuccess then [res["ETag"], JSON.parse(res.body)]
|
50
|
+
else raise_exception(res)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def gather_partial_content(api_req, res)
|
55
|
+
Heroku::Properties.logger.info("[Conn] Gathering Partial Content.")
|
56
|
+
|
57
|
+
list_head = JSON.parse(res.body)
|
58
|
+
etag, list_tail =
|
59
|
+
self.send(
|
60
|
+
api_req.method,
|
61
|
+
api_req.end_point,
|
62
|
+
range: res["Next-Range"]
|
63
|
+
)
|
64
|
+
|
65
|
+
list_tail.unshift(*list_head)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.raise_exception(res)
|
69
|
+
Heroku::Properties.logger.error("[Conn] Uh oh, something went wrong with request #{res["Request-Id"]}.")
|
70
|
+
raise res.class::EXCEPTION_TYPE.new(status(res.code), nil)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.status(code)
|
74
|
+
Hash[Net::HTTPResponse::CODE_TO_OBJ.map { |k, v| [k, v.to_s] }]
|
75
|
+
.merge({ "429" => "Net::HTTPTooManyRequests" }) # Ruby 1.9.3 shiv
|
76
|
+
.fetch(code, "Net::HTTPUnknownError")
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.headers(opts = {})
|
80
|
+
{
|
81
|
+
"Accept" => 'application/vnd.heroku+json; version=3',
|
82
|
+
"Content-Type" => 'application/json',
|
83
|
+
"Authorization" => Heroku::Properties.auth_token,
|
84
|
+
"User-Agent" => Heroku::Properties::USER_AGENT
|
85
|
+
}.merge({}.tap do |header|
|
86
|
+
header["If-None-Match"] = opts[:etag] if opts[:etag]
|
87
|
+
header["Range"] = opts[:range] if opts[:range]
|
88
|
+
end)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'heroku/properties'
|
2
|
+
|
3
|
+
class Heroku::Conn::Cache
|
4
|
+
CachePair = Struct.new(:response, :etag)
|
5
|
+
|
6
|
+
def initialize()
|
7
|
+
@response_cache = {}
|
8
|
+
@etag_pointers = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def put(r_type, new_etag, json)
|
12
|
+
pair = pair(r_type)
|
13
|
+
key = key(json)
|
14
|
+
old_etag, _ = pair.response[key]
|
15
|
+
record = [new_etag, json]
|
16
|
+
|
17
|
+
Heroku::Properties.logger.debug("[#{r_type} Cache] Caching #{key} #{new_etag}")
|
18
|
+
Heroku::Properties.logger.debug("[#{r_type} Cache] Dissociating tag: #{old_etag}")
|
19
|
+
|
20
|
+
pair.etag.delete(old_etag)
|
21
|
+
pair.response[key] = record
|
22
|
+
pair.etag[new_etag] = record
|
23
|
+
record
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch(r_type, etag)
|
27
|
+
Heroku::Properties.logger.info("[#{r_type} Cache] Fetching #{etag}")
|
28
|
+
pair(r_type).etag[etag]
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def pair(r_type)
|
34
|
+
CachePair[
|
35
|
+
@response_cache[r_type] ||= {},
|
36
|
+
@etag_pointers[r_type] ||= {}
|
37
|
+
]
|
38
|
+
end
|
39
|
+
|
40
|
+
def key(json_response)
|
41
|
+
case json_response
|
42
|
+
when Array then "list"
|
43
|
+
when Hash then json_response['id']
|
44
|
+
else nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/heroku/model.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'heroku/api/password'
|
2
|
+
require 'heroku/api/rate_limits'
|
3
|
+
require 'heroku/model/model_helper'
|
4
|
+
|
5
|
+
module Heroku
|
6
|
+
module Model
|
7
|
+
class Account < Struct.new(
|
8
|
+
:parent,
|
9
|
+
:id,
|
10
|
+
:email,
|
11
|
+
:verified,
|
12
|
+
:allow_tracking,
|
13
|
+
:beta,
|
14
|
+
:last_login,
|
15
|
+
:updated_at,
|
16
|
+
:created_at
|
17
|
+
)
|
18
|
+
|
19
|
+
include Heroku::Model::ModelHelper
|
20
|
+
include Heroku::API::Password
|
21
|
+
include Heroku::API::RateLimits
|
22
|
+
|
23
|
+
def inspect
|
24
|
+
"#<#{self.class.name} #{identifier}>"
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(params = {})
|
28
|
+
super(*struct_init_from_hash(params))
|
29
|
+
end
|
30
|
+
|
31
|
+
def patchable
|
32
|
+
sub_struct_as_hash(:email, :allow_tracking)
|
33
|
+
end
|
34
|
+
|
35
|
+
def identifiable
|
36
|
+
sub_struct_as_hash(:id, :email)
|
37
|
+
end
|
38
|
+
|
39
|
+
def save
|
40
|
+
parent.update_account(self)
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'heroku/model/model_helper'
|
2
|
+
require 'git'
|
3
|
+
|
4
|
+
module Heroku
|
5
|
+
module Model
|
6
|
+
class App < Struct.new(
|
7
|
+
:parent,
|
8
|
+
:id,
|
9
|
+
:name,
|
10
|
+
:owner,
|
11
|
+
:region,
|
12
|
+
:git_url,
|
13
|
+
:web_url,
|
14
|
+
:repo_size,
|
15
|
+
:slug_size,
|
16
|
+
:buildpack_provided_description,
|
17
|
+
:stack,
|
18
|
+
:maintenance,
|
19
|
+
:archived_at,
|
20
|
+
:created_at,
|
21
|
+
:released_at,
|
22
|
+
:updated_at
|
23
|
+
)
|
24
|
+
|
25
|
+
include Heroku::Model::ModelHelper
|
26
|
+
|
27
|
+
def inspect
|
28
|
+
"#<#{self.class.name} #{identifier}>"
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(params = {})
|
32
|
+
super(*struct_init_from_hash(params))
|
33
|
+
end
|
34
|
+
|
35
|
+
def push(dir)
|
36
|
+
begin
|
37
|
+
Git.open(dir, log: Heroku::Properties.logger).push(git_url)
|
38
|
+
true
|
39
|
+
rescue => e
|
40
|
+
Heroku::Properties.logger.error(e.message)
|
41
|
+
e.backtrace.each do |line|
|
42
|
+
Heroku::Properties.logger.error(line)
|
43
|
+
end
|
44
|
+
|
45
|
+
false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def patchable
|
50
|
+
sub_struct_as_hash(:maintenance, :name)
|
51
|
+
end
|
52
|
+
|
53
|
+
def identifiable
|
54
|
+
sub_struct_as_hash(:id, :name)
|
55
|
+
end
|
56
|
+
|
57
|
+
def end_point
|
58
|
+
"/apps/#{id}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def save
|
62
|
+
parent.update_app(self)
|
63
|
+
end
|
64
|
+
|
65
|
+
def destroy
|
66
|
+
parent.delete_app(self)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'heroku/api/app'
|
2
|
+
require 'heroku/model/array_proxy'
|
3
|
+
|
4
|
+
module Heroku
|
5
|
+
module Model
|
6
|
+
class AppList < Heroku::Model::ArrayProxy
|
7
|
+
include Heroku::API::App
|
8
|
+
|
9
|
+
def inspect
|
10
|
+
"#<Heroku::Model::Apps>"
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
case key
|
15
|
+
when String, Symbol then app(key.to_s)
|
16
|
+
else super(key)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Heroku
|
2
|
+
module Model
|
3
|
+
class ArrayProxy
|
4
|
+
def initialize(deferred_array)
|
5
|
+
@deferred_array = deferred_array
|
6
|
+
end
|
7
|
+
|
8
|
+
def method_missing(sym, *args)
|
9
|
+
begin
|
10
|
+
proxy_array.send(sym, *args)
|
11
|
+
rescue NoMethodError
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def all
|
17
|
+
proxy_array
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def proxy_array
|
23
|
+
@proxy_array ||= @deferred_array.call(self)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Heroku
|
2
|
+
module Model
|
3
|
+
module ModelHelper
|
4
|
+
def struct_init_from_hash(hash)
|
5
|
+
hash.values_at(*members.map(&:to_s))
|
6
|
+
end
|
7
|
+
|
8
|
+
def sub_struct_as_hash(*params)
|
9
|
+
Hash[(params & members).map { |k| [k, send(k)] }]
|
10
|
+
end
|
11
|
+
|
12
|
+
def identifier
|
13
|
+
identifiable.to_a.map { |k,v| "#{k}=#{v.inspect}"}.join(', ')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'heroku/version'
|
3
|
+
|
4
|
+
module Heroku
|
5
|
+
class Properties
|
6
|
+
require 'heroku/properties/null_logger'
|
7
|
+
|
8
|
+
USER_AGENT = "Heroku Platform API Gem #{Heroku::VERSION}"
|
9
|
+
@@auth_token = nil
|
10
|
+
@@logger = nil
|
11
|
+
|
12
|
+
def self.auth_token
|
13
|
+
@@auth_token
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.api_key=(key)
|
17
|
+
raise ArgumentError, "Need an API key" if key.nil?
|
18
|
+
@@auth_token = Base64.strict_encode64(":#{key}\n").strip
|
19
|
+
key
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.logger
|
23
|
+
@@logger || NullLogger.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.logger=(logger)
|
27
|
+
@@logger = logger
|
28
|
+
end
|
29
|
+
|
30
|
+
module ConfigMethods
|
31
|
+
def configure # yield
|
32
|
+
yield Heroku::Properties
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/heroku_api.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Heroku::Conn::Cache do
|
4
|
+
subject { Heroku::Conn::Cache.new }
|
5
|
+
|
6
|
+
describe "#put" do
|
7
|
+
let(:new_etag) { "new_etag" }
|
8
|
+
let(:old_etag) { "old_etag" }
|
9
|
+
let(:r_type) { "test" }
|
10
|
+
let(:json) { { a: 1 } }
|
11
|
+
let(:new_json) { { a: 2 } }
|
12
|
+
|
13
|
+
before do
|
14
|
+
subject.put(r_type, old_etag, json)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should update the cache value" do
|
18
|
+
subject.put(r_type, new_etag, new_json)
|
19
|
+
expect(subject.fetch(r_type, new_etag)).to eq([new_etag, new_json])
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should remove references to the old etag." do
|
23
|
+
subject.put(r_type, new_etag, new_json)
|
24
|
+
|
25
|
+
expect(subject.fetch(r_type, old_etag)).to be_nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Heroku::Conn do
|
4
|
+
|
5
|
+
describe ".check_response" do
|
6
|
+
context "when the response is unsuccessful" do
|
7
|
+
let(:unsuccessful_response) { Net::HTTPClientError.new() }
|
8
|
+
|
9
|
+
it "should raise a error" do
|
10
|
+
expect { described_class.send(:check_response, nil, nil, response) }.to raise_error
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Heroku::Model::ModelHelper do
|
4
|
+
let(:_Model) do
|
5
|
+
Class.new(Struct.new(:a, :b, :c)) do
|
6
|
+
include Heroku::Model::ModelHelper
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
subject { _Model[1, 2, 3] }
|
11
|
+
|
12
|
+
|
13
|
+
describe "#struct_init_from_hash" do
|
14
|
+
let(:valid_hash) { { "a" => 1, "b" => 2, "c" => 3 } }
|
15
|
+
let(:disorderd_hash) { { "b" => 2, "a" => 1, "c" => 3 }}
|
16
|
+
let(:invalid_hash) { { "d" => 4 } }
|
17
|
+
|
18
|
+
it "finds all the values from the hash that are members of the Model." do
|
19
|
+
init_list = subject.struct_init_from_hash(valid_hash)
|
20
|
+
expect(init_list).to match_array([1, 2, 3])
|
21
|
+
end
|
22
|
+
|
23
|
+
it "provides the values in the Model's order, not the hash's." do
|
24
|
+
init_list = subject.struct_init_from_hash(disorderd_hash)
|
25
|
+
expect(init_list).to eq([1, 2, 3])
|
26
|
+
end
|
27
|
+
|
28
|
+
it "ignores values from the has that are not members of the Model." do
|
29
|
+
init_list = subject.struct_init_from_hash(invalid_hash)
|
30
|
+
expect(init_list).not_to include(4)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#sub_struct_as_hash" do
|
35
|
+
let(:valid_params) { [:a, :b] }
|
36
|
+
let(:invalid_params) { [:d] }
|
37
|
+
|
38
|
+
it "returns a hash of the requisite parameters." do
|
39
|
+
hash = subject.sub_struct_as_hash(*valid_params)
|
40
|
+
expect(hash).to include(a: 1, b: 2)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "does not include paramters that are not requested." do
|
44
|
+
hash = subject.sub_struct_as_hash(*valid_params)
|
45
|
+
expect(hash).not_to include(:c)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "ignores parameters that do not exist." do
|
49
|
+
hash = subject.sub_struct_as_hash(*invalid_params)
|
50
|
+
expect(hash).not_to include(:d)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#identifier" do
|
55
|
+
context "when there are no identifiable parameters" do
|
56
|
+
before { subject.should_receive(:identifiable).and_return({}) }
|
57
|
+
its(:identifier) { should == "" }
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when the identifier is one parameter" do
|
61
|
+
before { subject.should_receive(:identifiable).and_return({ a: 1 }) }
|
62
|
+
its(:identifier) { should == "a=1" }
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when the identifier contains multiple parameters" do
|
66
|
+
before { subject.should_receive(:identifiable).and_return({ a: 1, b: 2}) }
|
67
|
+
its(:identifier) { should == "a=1, b=2" }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Heroku::Properties::NullLogger do
|
4
|
+
|
5
|
+
subject { described_class.new }
|
6
|
+
|
7
|
+
it { should respond_to(:unknown).with(1).argument }
|
8
|
+
it { should respond_to(:warn).with(1).argument }
|
9
|
+
it { should respond_to(:debug).with(1).argument }
|
10
|
+
it { should respond_to(:info).with(1).argument }
|
11
|
+
|
12
|
+
describe "#tagged" do
|
13
|
+
let(:block_check) { double("block_check", call: true) }
|
14
|
+
|
15
|
+
it { should respond_to(:tagged).with(1).argument }
|
16
|
+
it "calls the block provided to it" do
|
17
|
+
block_check.should_receive(:call).once
|
18
|
+
|
19
|
+
subject.tagged("tag") do
|
20
|
+
block_check.call
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Heroku::Properties do
|
4
|
+
|
5
|
+
it "should respond to #auth_token" do
|
6
|
+
expect(described_class).to respond_to(:auth_token)
|
7
|
+
end
|
8
|
+
|
9
|
+
describe "#api_key=" do
|
10
|
+
let(:api_key) { "01234567-89ab-cdef-0123-456789abcdef" }
|
11
|
+
|
12
|
+
it "should create a base64 encoded auth token" do
|
13
|
+
described_class.api_key = api_key
|
14
|
+
expect(described_class.auth_token).to eq(Base64.encode64(":#{api_key}\n").strip)
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when no api_key is provided" do
|
18
|
+
let(:api_key) { nil }
|
19
|
+
|
20
|
+
it "should raise an error" do
|
21
|
+
expect { described_class.api_key = api_key }.to raise_error(ArgumentError)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should leave the auth_token unchanged" do
|
25
|
+
expect {
|
26
|
+
begin
|
27
|
+
described_class.api_key = api_key
|
28
|
+
rescue ArgumentError
|
29
|
+
end
|
30
|
+
}.not_to change(described_class, :auth_token)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when no logger is provided" do
|
36
|
+
before do
|
37
|
+
described_class.logger = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should provide a null logger" do
|
41
|
+
expect(described_class.logger).not_to be_nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Bundler.setup
|
2
|
+
|
3
|
+
require 'heroku_api'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
7
|
+
config.run_all_when_everything_filtered = true
|
8
|
+
config.filter_run :focus
|
9
|
+
|
10
|
+
# Run specs in random order to surface order dependencies. If you find an
|
11
|
+
# order dependency and want to debug it, you can fix the order by providing
|
12
|
+
# the seed, which is printed after each run.
|
13
|
+
# --seed 1234
|
14
|
+
config.order = 'random'
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: heroku-platform-api
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ashok Menon
|
8
|
+
- Rentify
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-08-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: git
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ! '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: bundler
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ~>
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '1.3'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ~>
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '1.3'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rake
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
description: Create, destroy and manage your heroku applications programmatically,
|
71
|
+
using the Heroku Platform API.
|
72
|
+
email:
|
73
|
+
- amenon94@gmail.com
|
74
|
+
- dev@rentify.com
|
75
|
+
executables: []
|
76
|
+
extensions: []
|
77
|
+
extra_rdoc_files: []
|
78
|
+
files:
|
79
|
+
- .gitignore
|
80
|
+
- .rspec
|
81
|
+
- Gemfile
|
82
|
+
- LICENSE.txt
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- heroku.gemspec
|
86
|
+
- lib/heroku/api.rb
|
87
|
+
- lib/heroku/api/account.rb
|
88
|
+
- lib/heroku/api/app.rb
|
89
|
+
- lib/heroku/api/apps.rb
|
90
|
+
- lib/heroku/api/password.rb
|
91
|
+
- lib/heroku/api/rate_limits.rb
|
92
|
+
- lib/heroku/api/regions.rb
|
93
|
+
- lib/heroku/conn.rb
|
94
|
+
- lib/heroku/conn/cache.rb
|
95
|
+
- lib/heroku/model.rb
|
96
|
+
- lib/heroku/model/account.rb
|
97
|
+
- lib/heroku/model/app.rb
|
98
|
+
- lib/heroku/model/app_list.rb
|
99
|
+
- lib/heroku/model/array_proxy.rb
|
100
|
+
- lib/heroku/model/model_helper.rb
|
101
|
+
- lib/heroku/properties.rb
|
102
|
+
- lib/heroku/properties/null_logger.rb
|
103
|
+
- lib/heroku/version.rb
|
104
|
+
- lib/heroku_api.rb
|
105
|
+
- spec/heroku/conn/cache_spec.rb
|
106
|
+
- spec/heroku/conn_spec.rb
|
107
|
+
- spec/heroku/model/model_helper_spec.rb
|
108
|
+
- spec/heroku/properties/null_logger_spec.rb
|
109
|
+
- spec/heroku/properties_spec.rb
|
110
|
+
- spec/spec_helper.rb
|
111
|
+
homepage: ''
|
112
|
+
licenses:
|
113
|
+
- MIT
|
114
|
+
metadata: {}
|
115
|
+
post_install_message:
|
116
|
+
rdoc_options: []
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ! '>='
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ! '>='
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
requirements: []
|
130
|
+
rubyforge_project:
|
131
|
+
rubygems_version: 2.0.6
|
132
|
+
signing_key:
|
133
|
+
specification_version: 4
|
134
|
+
summary: Ruby client for the Heroku Platform API.
|
135
|
+
test_files:
|
136
|
+
- spec/heroku/conn/cache_spec.rb
|
137
|
+
- spec/heroku/conn_spec.rb
|
138
|
+
- spec/heroku/model/model_helper_spec.rb
|
139
|
+
- spec/heroku/properties/null_logger_spec.rb
|
140
|
+
- spec/heroku/properties_spec.rb
|
141
|
+
- spec/spec_helper.rb
|