Skip to content

Commit daeb438

Browse files
author
Kenneth Reitz
committed
addons wrapped.
1 parent ef7b597 commit daeb438

File tree

4 files changed

+218
-3
lines changed

4 files changed

+218
-3
lines changed

heroku/api.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
"""
99

1010
import requests
11-
11+
from .compat import json
12+
from .models import *
1213

1314
HEROKU_URL = 'https://api.heroku.com'
1415

@@ -54,8 +55,41 @@ def _verify_api_key(self):
5455
return self._api_key_verified
5556

5657
def _url_for(self, *args):
58+
args = map(str, args)
5759
return '/'.join([self._heroku_url] + list(args))
5860

61+
@staticmethod
62+
def _resource_serialize(o):
63+
"""Returns JSON serialization of given object."""
64+
return json.dumps(o)
65+
66+
@staticmethod
67+
def _resource_deserialize(s):
68+
"""Returns dict deserialization of a given JSON string."""
69+
70+
try:
71+
return json.loads(s)
72+
except ValueError:
73+
raise ResponseError('The API Response was not valid.')
74+
75+
76+
def _get_resource(self, resource, obj, **kwargs):
77+
78+
r = self._http_resource('GET', resource, params=kwargs)
79+
item = self._resource_deserialize(r.content)
80+
81+
return obj.new_from_dict(item, gh=self)
82+
83+
84+
def _get_resources(self, resource, obj, **kwargs):
85+
86+
url = self._url_for(resource)
87+
r = self._s.get(url, params=kwargs)
88+
89+
d_items = self._resource_deserialize(r.content)
90+
91+
return [obj.new_from_dict(item, h=self) for item in d_items]
92+
5993

6094
class Heroku(HerokuCore):
6195
"""The main Heroku class."""
@@ -66,6 +100,10 @@ def __init__(self):
66100
def __repr__(self):
67101
return '<heroku-client at 0x%x>' % (id(self))
68102

103+
def addons(self):
104+
# r = self._s.get(self._url_for('addons'))
105+
return self._get_resources(('addons'), Addon)
106+
69107
def apps(self):
70108
print self._url_for('apps')
71109

heroku/compat.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
heroku.compat
5+
~~~~~~~~~~~~~
6+
7+
Compatiblity for heroku.py.
8+
"""
9+
10+
try:
11+
import json
12+
except ImportError:
13+
import simplejson as json

heroku/helpers.py

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# -*- coding: utf-8 -*-
2+
3+
"""
4+
heroku.helpers
5+
~~~~~~~~~~~~~~
6+
7+
This module contians the helpers.
8+
"""
9+
10+
from datetime import datetime
11+
12+
from dateutil.parser import parse as parse_datetime
13+
14+
def is_collection(obj):
15+
"""Tests if an object is a collection."""
16+
17+
col = getattr(obj, '__getitem__', False)
18+
val = False if (not col) else True
19+
20+
if isinstance(obj, basestring):
21+
val = False
22+
23+
return val
24+
25+
26+
27+
# from kennethreitz/python-github3
28+
def to_python(obj,
29+
in_dict,
30+
str_keys=None,
31+
date_keys=None,
32+
int_keys=None,
33+
object_map=None,
34+
bool_keys=None, **kwargs):
35+
"""Extends a given object for API Consumption.
36+
37+
:param obj: Object to extend.
38+
:param in_dict: Dict to extract data from.
39+
:param string_keys: List of in_dict keys that will be extracted as strings.
40+
:param date_keys: List of in_dict keys that will be extrad as datetimes.
41+
:param object_map: Dict of {key, obj} map, for nested object results.
42+
"""
43+
44+
d = dict()
45+
46+
if str_keys:
47+
for in_key in str_keys:
48+
d[in_key] = in_dict.get(in_key)
49+
50+
if date_keys:
51+
for in_key in date_keys:
52+
in_date = in_dict.get(in_key)
53+
try:
54+
out_date = datetime.strptime(in_date, '%Y-%m-%dT%H:%M:%SZ')
55+
except TypeError:
56+
out_date = None
57+
58+
d[in_key] = out_date
59+
60+
if int_keys:
61+
for in_key in int_keys:
62+
if (in_dict is not None) and (in_dict.get(in_key) is not None):
63+
d[in_key] = int(in_dict.get(in_key))
64+
65+
if bool_keys:
66+
for in_key in bool_keys:
67+
if in_dict.get(in_key) is not None:
68+
d[in_key] = bool(in_dict.get(in_key))
69+
70+
if object_map:
71+
for (k, v) in object_map.items():
72+
if in_dict.get(k):
73+
d[k] = v.new_from_dict(in_dict.get(k))
74+
75+
obj.__dict__.update(d)
76+
obj.__dict__.update(kwargs)
77+
78+
# Save the dictionary, for write comparisons.
79+
# obj._cache = d
80+
# obj.__cache = in_dict
81+
82+
return obj
83+
84+
85+
# from kennethreitz/python-github3
86+
def to_api(in_dict, int_keys=None, date_keys=None, bool_keys=None):
87+
"""Extends a given object for API Production."""
88+
89+
# Cast all int_keys to int()
90+
if int_keys:
91+
for in_key in int_keys:
92+
if (in_key in in_dict) and (in_dict.get(in_key, None) is not None):
93+
in_dict[in_key] = int(in_dict[in_key])
94+
95+
# Cast all date_keys to datetime.isoformat
96+
if date_keys:
97+
for in_key in date_keys:
98+
if (in_key in in_dict) and (in_dict.get(in_key, None) is not None):
99+
100+
_from = in_dict[in_key]
101+
102+
if isinstance(_from, basestring):
103+
dtime = parse_datetime(_from)
104+
105+
elif isinstance(_from, datetime):
106+
dtime = _from
107+
108+
in_dict[in_key] = dtime.isoformat()
109+
110+
elif (in_key in in_dict) and in_dict.get(in_key, None) is None:
111+
del in_dict[in_key]
112+
113+
# Remove all Nones
114+
for k, v in in_dict.items():
115+
if v is None:
116+
del in_dict[k]
117+
118+
return in_dict

heroku/models.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,60 @@
77
This module contains the models that comprise the Heroku API.
88
"""
99

10+
from .helpers import to_python, to_api
11+
1012
class BaseResource(object):
13+
14+
_strs = []
15+
_ints = []
16+
_dates = []
17+
_bools = []
18+
_map = {}
19+
# _writeable = []
20+
# _cache = {}
21+
1122
def __init__(self):
23+
self._bootstrap()
1224
super(BaseResource, self).__init__()
1325

26+
def _bootstrap(self):
27+
"""Bootstraps the model object based on configured values."""
28+
29+
for attr in self._keys():
30+
setattr(self, attr, None)
31+
32+
def _keys(self):
33+
return self._strs + self._ints + self._dates + self._bools + self._map.keys()
34+
35+
def dict(self):
36+
d = dict()
37+
for k in self.keys():
38+
d[k] = self.__dict__.get(k)
39+
40+
return d
41+
42+
43+
@classmethod
44+
def new_from_dict(cls, d, h=None):
45+
46+
return to_python(
47+
obj=cls(), in_dict=d,
48+
str_keys = cls._strs,
49+
int_keys = cls._ints,
50+
date_keys = cls._dates,
51+
bool_keys = cls._bools,
52+
object_map = cls._map,
53+
_h = h
54+
)
55+
1456

1557
class Addon(BaseResource):
16-
def __init__(self):
17-
super(App, self).__init__()
58+
59+
_strs = ['name', 'description', 'url', 'state']
60+
_bools = ['beta',]
61+
62+
def __repr__(self):
63+
return "<addon '%s'>" % (self.name)
1864

1965

2066
class App(BaseResource):

0 commit comments

Comments
 (0)