Last active
March 9, 2018 17:28
-
-
Save jpic/9f9ef74e5ab739b4ffd04ecc373d1d55 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class NameAdapter(StringAdapter): | |
# steps always executed in clean clone ! | |
def validate(self): | |
if allcaps(self.data): | |
self.errors.append('omg poney') | |
def clean(self): | |
self.data = self.data.capitalize() | |
assert NameAdapter(data='AUO').validate().errors == ['omg poney'] | |
# declarative syntax, without metaclass | |
class PersonAdapter(DictAdapter): | |
map = dict( | |
name=NameAdapter(), | |
# instead of methods, AdapterInterface understands list of adapters | |
fakename=StringAdapter(clean=[NameAdapter.validate]), | |
# instead of methods, AdapterInterface understands list of callbacks | |
age=IntAdapter(clean=[is_numeric, cast_int, greater_than(0)]), | |
hobbies=adapters.factory(relation=PersonModel.hobbies), | |
# the above makes an adapter that accepts relation=RelatedFieldManager | |
extra=DictAdapter(map=dict(.......)) # recursiveness | |
) | |
adapters = [NoBadwordInString(), BootstrapFormAdapter(), RestAdapter()] | |
def mutate(self): | |
'''called when the adapter is instanciated, or added to an adapter, or before a step executes, to keep fresh''' | |
super().mutate() # call other adapters mutate() recursively ! | |
for key, value in self.map.items(): # introspection | |
quacks = getattr(value, 'quack', False) | |
if quacks: # leads to mutation mutation: new field example | |
self.map.quack_extra = DuckAdapter(option='duckperson') | |
super().mutate() # let's do it again in case an adapter has some feature for quack_extra ? | |
def process(self): | |
super().process() # execute all own and maped adapters process() yes recursive | |
self.instance.__dict__ = self.data # if clean passed, we haz self.data ! | |
self.instance.save() # or something like that | |
process.require_step_success = ['clean'] | |
def validate(self): | |
# call validate() on self.adapters and on mapped adapters, recursion ! | |
super().validate() | |
if something(self.instance): | |
# will this be moved in its own adapter? time'll tell | |
self.errors.append('something happened') | |
def clean(self): | |
super().clean() # clean everything | |
self.data['alsoadd'] = somesecrets() | |
def render(self): | |
return my_custom_render_step(self.request, self) | |
render.require_variables = ['request'] | |
def instanciate(self): | |
# i should have added ModelAdapter instead of doing this ! | |
if getattr(self, 'pk', None): | |
self.instance = Person.objects.get(pk=self.pk) | |
elif getattr(self, 'data', None): | |
self.instance = Person(**self.data) | |
else: | |
self.instance = Person() | |
def initialize(self): | |
if getattr(self, 'instance', None): | |
self.initial = self.instance.__dict__ # lol naive | |
else: | |
super().initialize() | |
def response(self): | |
# this would be automatic but is here for the example i'm poneying my way out | |
# because where is DjangoRequestResponse adapter ? | |
# well not as far as you might think | |
if self.adapters.RestAdapter.adapts(): # can self.request.is_ajax or 'MAGIC' in self.request.pathinfo() | |
self.adapters.RestAdapter.response() # sets self.response of course ! | |
if self.adapters.BootstrapFormAdapter.adapts(): | |
self.adapters.BootstrapFormAdapter.render() # sets self.rendered of course ! | |
self.adapters.add('TemplateAdapter', clone=False) | |
self.template = 'lol.html' | |
# set self.rendered, after using self.rendered in the template of course ! | |
# but it could use self.instance if it wanted to ! | |
self.adapters.TemplateAdapter.render() | |
else: | |
# set some response ! | |
self.response = Response('wtf you poney !') | |
response.require_variables = ['request'] | |
def adapts(self): # used by factory | |
return isinstance(getattr(self, 'instance', None), PersonModel) | |
# steps magical call method will actualy clone the adapter | |
# and add the new arguments passed to the step to the adapter's state | |
# and call mutate on all adapters | |
# and call the adapter's method | |
# to execute all call super().yourmethod() | |
a = PersonAdapter().steps.validate(data={'name':'AOU'}) | |
assert a.errors = dict(map=dict(name='omg poney')) | |
a = a.steps.clean(data={'name':'aoeu'}) | |
assert not a.instance | |
a = a.steps.process() | |
assert a.instance.pk | |
a = a.steps.response() | |
assert a.response | |
PersonAdapter(instance=PersonModel()).steps.render(request=request).response | |
# or adapters.register(PersoneAdapter); adapters.factory(instance=person).steps.render ... thx to PersonAdapter.adapts ! | |
class PersonQuerysetAdapter(ListAdapter): | |
map = [PersonAdapter] # one PersonAdapter per list item ! | |
# more steps overrides, more adapters | |
def adapts(self): | |
# why did i not add QuerySetAdapter ? For the sake of the example and Poney ! | |
return self.queryset.model == Persone | |
adapts.require_variables = 'queryeset' | |
adapters.register(PersonListAdapter) | |
adapters.factory(queryset=Person.objects.none()) # build adapter for |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment