Choć sam konta w Książeczce Maryja nie posiadam, przyszło mi ostatnio ścierać się z ichnim API, coby dla klienta wdrożenia poczynić. Po przejrzeniu dostępnych bibliotek (w tym dość żałosnego python-sdk), stanęło na dość popularnym projekcie pyfacebook.
Oryginalne repozytorium było wybrakowane pod względem funkcjonalności, wybraliśmy więc fork, który z pewnością stworzył fan Władcy Pierścieni. Jak się miało później okazać, "jeden, by wszystkie zgromadzić i w ciemności związać" dość wiernie oddaje uzyskany efekt.
Człowiek głupi, to przy pierwszym problemie zajrzał w bebechy ofiary. Po tym stanął jak wryty i całe Satchmo przeleciało mu przed oczami. Na początek tradycyjny problem — przejście się w glanach po separacji warstw:
from threading import local
_thread_locals = local()
def get_facebook_client():
"""
Get the current Facebook object for the calling thread.
"""
try:
return _thread_locals.facebook
except AttributeError:
raise ImproperlyConfigured('Make sure you have the Facebook middleware installed.')
class FacebookMiddleware(object):
"""
Middleware that attaches a Facebook object to every incoming request.
The Facebook object created can also be accessed from models for the
current thread by using get_facebook_client().
callback_path can be a string or a callable. Using a callable lets us
pass in something like lambda reverse('our_canvas_view') so we can follow
the DRY principle.
"""
# ...
def process_request(self, request):
# ...
_thread_locals.facebook = request.facebook = Facebook(self.api_key,
self.secret_key, app_name=self.app_name,
callback_path=callback_path, internal=self.internal,
proxy=self.proxy, app_id=self.app_id, oauth2=self.oauth2)
Przerażające konstrukcje zaczęły się jednak później:
# generate the Facebook proxies
def __generate_proxies():
for namespace in METHODS:
methods = {}
for method, param_data in METHODS[namespace].iteritems():
methods[method] = __generate_facebook_method(namespace, method, param_data)
proxy = type('%sProxy' % namespace.title(), (Proxy,), methods)
globals()[proxy.__name__] = proxy
__generate_proxies()
class Facebook(object):
"""
Provides access to the Facebook API.
...
"""
def __init__(self, api_key, secret_key, auth_token=None, app_name=None,
callback_path=None, internal=None, proxy=None,
facebook_url=None, facebook_secure_url=None,
generate_session_secret=0, app_id=None, oauth2=False):
# ...
for namespace in METHODS:
self.__dict__[namespace] = eval('%sProxy(self, \'%s\')' % (namespace.title(), 'facebook.%s' % namespace))
Skończyło się na własnym forku i refaktoryzacji tych i wielu innych fragmentów kodu. Poprawioną wersję można znaleźć na GitHubie.
Na koniec stare powiedzenie ludowe:
Gdy bowiem zoczysz, iż jest coś narzeczy, a za cwanego masz się i uważasz
__globals__
ieval()
za sprawy rozwiązanie, mylisz się wielce, przeto idź przypudrować nos¹.
¹ Ciało, 2003