Skip to content

Commit 59a244b

Browse files
committed
Move new dajaxice into place.
- Legacy-Id: 7623
1 parent 4a2c615 commit 59a244b

66 files changed

Lines changed: 2920 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

dajaxice/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__version__ = (0, 5, 5, 'beta')

dajaxice/core/Dajaxice.py

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import logging
2+
3+
from django.utils.importlib import import_module
4+
5+
log = logging.getLogger('dajaxice')
6+
7+
8+
class DajaxiceFunction(object):
9+
""" Basic representation of a dajaxice ajax function."""
10+
11+
def __init__(self, function, name, method):
12+
self.function = function
13+
self.name = name
14+
self.method = method
15+
16+
def call(self, *args, **kwargs):
17+
""" Call the function. """
18+
return self.function(*args, **kwargs)
19+
20+
21+
class DajaxiceModule(object):
22+
""" Basic representation of a dajaxice module. """
23+
24+
def __init__(self, name=None):
25+
self.name = name
26+
self.functions = {}
27+
self.submodules = {}
28+
29+
def add(self, name, function):
30+
""" Add this function at the ``name`` deep. If the submodule already
31+
exists, recusively call the add method into the submodule. If not,
32+
create the module and call the add method."""
33+
34+
# If this is not the final function name (there are more modules)
35+
# split the name again an register a new submodule.
36+
if '.' in name:
37+
module, extra = name.split('.', 1)
38+
if module not in self.submodules:
39+
self.submodules[module] = DajaxiceModule(module)
40+
self.submodules[module].add(extra, function)
41+
else:
42+
self.functions[name] = function
43+
44+
45+
class Dajaxice(object):
46+
47+
def __init__(self):
48+
self._registry = {}
49+
self._modules = None
50+
51+
def register(self, function, name=None, method='POST'):
52+
"""
53+
Register this function as a dajaxice function.
54+
55+
If no name is provided, the module and the function name will be used.
56+
The final (customized or not) must be unique. """
57+
58+
method = self.clean_method(method)
59+
60+
# Generate a default name
61+
if not name:
62+
module = ''.join(str(function.__module__).rsplit('.ajax', 1))
63+
name = '.'.join((module, function.__name__))
64+
65+
if ':' in name:
66+
log.error('Ivalid function name %s.' % name)
67+
return
68+
69+
# Check for already registered functions
70+
if name in self._registry:
71+
log.error('%s was already registered.' % name)
72+
return
73+
74+
# Create the dajaxice function.
75+
function = DajaxiceFunction(function=function,
76+
name=name,
77+
method=method)
78+
79+
# Register this new ajax function
80+
self._registry[name] = function
81+
82+
def is_callable(self, name, method):
83+
""" Return if the function callable or not. """
84+
return name in self._registry and self._registry[name].method == method
85+
86+
def clean_method(self, method):
87+
""" Clean the http method. """
88+
method = method.upper()
89+
if method not in ['GET', 'POST']:
90+
method = 'POST'
91+
return method
92+
93+
def get(self, name):
94+
""" Return the dajaxice function."""
95+
return self._registry[name]
96+
97+
@property
98+
def modules(self):
99+
""" Return an easy to loop module hierarchy with all the functions."""
100+
if not self._modules:
101+
self._modules = DajaxiceModule()
102+
for name, function in self._registry.items():
103+
self._modules.add(name, function)
104+
return self._modules
105+
106+
LOADING_DAJAXICE = False
107+
108+
109+
def dajaxice_autodiscover():
110+
"""
111+
Auto-discover INSTALLED_APPS ajax.py modules and fail silently when
112+
not present. NOTE: dajaxice_autodiscover was inspired/copied from
113+
django.contrib.admin autodiscover
114+
"""
115+
global LOADING_DAJAXICE
116+
if LOADING_DAJAXICE:
117+
return
118+
LOADING_DAJAXICE = True
119+
120+
import imp
121+
from django.conf import settings
122+
123+
for app in settings.INSTALLED_APPS:
124+
125+
try:
126+
app_path = import_module(app).__path__
127+
except AttributeError:
128+
continue
129+
130+
try:
131+
imp.find_module('ajax', app_path)
132+
except ImportError:
133+
continue
134+
135+
import_module("%s.ajax" % app)
136+
137+
LOADING_DAJAXICE = False

dajaxice/core/__init__.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from django.conf import settings
2+
3+
from Dajaxice import Dajaxice, dajaxice_autodiscover
4+
5+
6+
class DajaxiceConfig(object):
7+
""" Provide an easy to use way to read the dajaxice configuration and
8+
return the default values if no configuration is present."""
9+
10+
default_config = {'DAJAXICE_XMLHTTPREQUEST_JS_IMPORT': True,
11+
'DAJAXICE_JSON2_JS_IMPORT': True,
12+
'DAJAXICE_EXCEPTION': 'DAJAXICE_EXCEPTION',
13+
'DAJAXICE_MEDIA_PREFIX': 'dajaxice'}
14+
15+
def __getattr__(self, name):
16+
""" Return the customized value for a setting (if it exists) or the
17+
default value if not. """
18+
19+
if name in self.default_config:
20+
if hasattr(settings, name):
21+
return getattr(settings, name)
22+
return self.default_config.get(name)
23+
return None
24+
25+
@property
26+
def dajaxice_url(self):
27+
return r'^%s/' % self.DAJAXICE_MEDIA_PREFIX
28+
29+
@property
30+
def django_settings(self):
31+
return settings
32+
33+
@property
34+
def modules(self):
35+
return dajaxice_functions.modules
36+
37+
dajaxice_functions = Dajaxice()
38+
dajaxice_config = DajaxiceConfig()

dajaxice/decorators.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import functools
2+
3+
from dajaxice.core import dajaxice_functions
4+
5+
6+
def dajaxice_register(*dargs, **dkwargs):
7+
""" Register some function as a dajaxice function
8+
9+
For legacy purposes, if only a function is passed register it a simple
10+
single ajax function using POST, i.e:
11+
12+
@dajaxice_register
13+
def ajax_function(request):
14+
...
15+
16+
After 0.5, dajaxice allow to customize the http method and the final name
17+
of the registered function. This decorator covers both the legacy and
18+
the new functionality, i.e:
19+
20+
@dajaxice_register(method='GET')
21+
def ajax_function(request):
22+
...
23+
24+
@dajaxice_register(method='GET', name='my.custom.name')
25+
def ajax_function(request):
26+
...
27+
28+
You can also register the same function to use a different http method
29+
and/or use a different name.
30+
31+
@dajaxice_register(method='GET', name='users.get')
32+
@dajaxice_register(method='POST', name='users.update')
33+
def ajax_function(request):
34+
...
35+
"""
36+
37+
if len(dargs) and not dkwargs:
38+
function = dargs[0]
39+
dajaxice_functions.register(function)
40+
return function
41+
42+
def decorator(function):
43+
@functools.wraps(function)
44+
def wrapper(request, *args, **kwargs):
45+
return function(request, *args, **kwargs)
46+
dajaxice_functions.register(function, *dargs, **dkwargs)
47+
return wrapper
48+
return decorator

dajaxice/exceptions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
class DajaxiceError(Exception):
2+
pass
3+
4+
5+
class FunctionNotCallableError(DajaxiceError):
6+
pass
7+
8+
9+
class DajaxiceImportError(DajaxiceError):
10+
pass

dajaxice/finders.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import os
2+
import tempfile
3+
4+
from django.contrib.staticfiles import finders
5+
from django.template import Context
6+
from django.template.loader import get_template
7+
from django.core.exceptions import SuspiciousOperation
8+
9+
10+
class VirtualStorage(finders.FileSystemStorage):
11+
"""" Mock a FileSystemStorage to build tmp files on demand."""
12+
13+
def __init__(self, *args, **kwargs):
14+
self._files_cache = {}
15+
super(VirtualStorage, self).__init__(*args, **kwargs)
16+
17+
def get_or_create_file(self, path):
18+
if path not in self.files:
19+
return ''
20+
21+
data = getattr(self, self.files[path])()
22+
23+
try:
24+
current_file = open(self._files_cache[path])
25+
current_data = current_file.read()
26+
current_file.close()
27+
if current_data != data:
28+
os.remove(path)
29+
raise Exception("Invalid data")
30+
except Exception:
31+
handle, tmp_path = tempfile.mkstemp()
32+
tmp_file = open(tmp_path, 'w')
33+
tmp_file.write(data)
34+
tmp_file.close()
35+
self._files_cache[path] = tmp_path
36+
37+
return self._files_cache[path]
38+
39+
def exists(self, name):
40+
return name in self.files
41+
42+
def listdir(self, path):
43+
folders, files = [], []
44+
for f in self.files:
45+
if f.startswith(path):
46+
f = f.replace(path, '', 1)
47+
if os.sep in f:
48+
folders.append(f.split(os.sep, 1)[0])
49+
else:
50+
files.append(f)
51+
return folders, files
52+
53+
def path(self, name):
54+
try:
55+
path = self.get_or_create_file(name)
56+
except ValueError:
57+
raise SuspiciousOperation("Attempted access to '%s' denied." % name)
58+
return os.path.normpath(path)
59+
60+
61+
class DajaxiceStorage(VirtualStorage):
62+
63+
files = {os.path.join('dajaxice', 'dajaxice.core.js'): 'dajaxice_core_js'}
64+
65+
def dajaxice_core_js(self):
66+
from dajaxice.core import dajaxice_autodiscover, dajaxice_config
67+
68+
dajaxice_autodiscover()
69+
70+
c = Context({'dajaxice_config': dajaxice_config})
71+
return get_template(os.path.join('dajaxice', 'dajaxice.core.js')).render(c)
72+
73+
74+
class DajaxiceFinder(finders.BaseStorageFinder):
75+
storage = DajaxiceStorage()

dajaxice/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Don't delete me

0 commit comments

Comments
 (0)